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
9 changes: 4 additions & 5 deletions .github/make_ci_environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def parse_pyproject(path: str, optional_sections_to_skip=None):
# numpy is also listed in build requirements with a higher version number
# so we skip it here to avoid conflicts.
continue
if "mkl" in dep:
# mkl is pinned to the exact version under test below instead.
continue
deps.add(dep)

# optional dependencies (PEP 621)
Expand All @@ -26,10 +29,6 @@ def parse_pyproject(path: str, optional_sections_to_skip=None):
continue
deps.update(group_deps)

deps.discard("geoana[all]")
deps.discard("geoana[doc,all]")
deps.discard("geoana[plot,extras,jittable]")

if "matplotlib" in deps:
deps.remove("matplotlib")
deps.add("matplotlib-base")
Expand Down Expand Up @@ -77,7 +76,7 @@ def create_env_yaml(deps, name="env", python_version=None, free_threaded=False):

deps = parse_pyproject(pyproject_path, optional_sections_to_skip=optional_to_skip)
deps.add("mkl-devel")
deps.add("pkg-config")
deps.add("cmake")
deps.add(f"mkl={mkl_vers}")
env_data = create_env_yaml(deps, name=env_name, python_version=py_vers, free_threaded=is_free_threaded)

Expand Down
85 changes: 85 additions & 0 deletions .github/strip_wheel_dependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""Remove a dependency from every wheel in a directory, in place.

These wheels statically link MKL (see [tool.cibuildwheel] in
pyproject.toml), so they have no runtime dependency on the `mkl`
package - but `dependencies` is static and shared with the source
build, which does need it. Strips it from these wheels after the fact,
since meson-python can't make it conditional on how a wheel was built.

Invoked from `repair-wheel-command` (pyproject.toml) as the last step
after auditwheel/delvewheel, directly on cibuildwheel's own
`{dest_dir}` - takes a directory rather than explicit wheel paths so
the same invocation works from both sh (Linux) and cmd.exe (Windows),
which doesn't glob `*.whl` itself.
"""

import argparse
import re
import subprocess
import sys
import tempfile
from pathlib import Path


def strip_dependency(wheel_path: Path, dependency_name: str) -> None:
dest_dir = wheel_path.parent

with tempfile.TemporaryDirectory() as tmp:
tmp = Path(tmp)
# Shell out to `wheel`'s CLI rather than importing wheel.cli.unpack/
# pack directly: wheel 0.46 made that package private with no
# compat shim (it's wheel._commands now, and could move again) -
# `python -m wheel unpack`/`pack` is the interface it actually
# keeps stable across versions.
subprocess.run(
[sys.executable, "-m", "wheel", "unpack", str(wheel_path), "--dest", str(tmp)],
check=True,
)
(unpacked_dir,) = tmp.iterdir()

dist_info = next(unpacked_dir.glob("*.dist-info"))
metadata_path = dist_info / "METADATA"
text = metadata_path.read_text(encoding="utf-8")

pattern = re.compile(rf"^Requires-Dist: {re.escape(dependency_name)}(\W.*)?$\n", re.MULTILINE)
new_text, count = pattern.subn("", text)
if count == 0:
raise SystemExit(f"'{dependency_name}' Requires-Dist not found in {wheel_path.name}; nothing to strip")

metadata_path.write_text(new_text, encoding="utf-8")

# Repacking to the same directory overwrites wheel_path in place:
# name/version/tags are unchanged, so the output filename matches,
# and by this point everything's already been read out of it.
result = subprocess.run(
[sys.executable, "-m", "wheel", "pack", str(unpacked_dir), "--dest-dir", str(dest_dir)],
check=True,
capture_output=True,
text=True,
)
print(result.stdout, end="")

# Sanity-check the output path wheel pack reports against wheel_path
# (parsed from its own report, not re-globbed: dest_dir may hold other
# wheels sharing this filename prefix, e.g. one per Python version).
match = re.search(r"^Repacking wheel as (?P<path>.+)\.\.\.", result.stdout, re.MULTILINE)
if match is None:
raise SystemExit(f"Couldn't parse the output wheel path from `wheel pack`'s output: {result.stdout!r}")
out_path = Path(match.group("path"))
if out_path != wheel_path:
raise SystemExit(f"Expected repacking to overwrite {wheel_path}, but it wrote {out_path} instead")


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("wheel_dir", type=Path, help="Directory of wheel(s) to process in place")
parser.add_argument("--dependency", default="mkl", help="Dependency name to strip (default: mkl)")
args = parser.parse_args()

for wheel_path in sorted(args.wheel_dir.glob("*.whl")):
strip_dependency(wheel_path, args.dependency)
print(f"stripped '{args.dependency}' from {wheel_path.name}", file=sys.stderr)


if __name__ == "__main__":
main()
183 changes: 84 additions & 99 deletions .github/workflows/python-package-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,130 +16,115 @@ jobs:
fail-fast: False
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.11", "3.12", "3.13", "3.14"]
mkl-version: ['2024', '2025']
python-version: ["3.12", "3.13", "3.14"]
mkl-version: ["2025", "2026"]
free-threaded: [true, false]
include:
- os: ubuntu-latest
python-version: "3.12"
mkl-version: "2024"
mkl-version: "2025"
coverage: true
- os: macos-15-intel
python-version: "3.11"
python-version: "3.12"
# Intel has not published MKL for macOS past 2023.2.
mkl-version: "2023"
free-threaded:
- os: macos-15-intel
python-version: "3.12"
python-version: "3.13"
mkl-version: "2023"
free-threaded:
exclude:
- python-version: "3.11"
free-threaded: true
# Free-threaded builds don't exist prior to Python 3.13.
- python-version: "3.12"
free-threaded: true
# Don't build free-threaded versions for experimental Python 3.13.
- python-version: "3.13"
free-threaded: true
mkl-version: "2025"
- python-version: "3.14"
mkl-version: "2025"

steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.13"

- name: Create Conda environment file
run: |
python -m pip install pyyaml
python .github/make_ci_environ.py
env:
PYTHON_VERSION: ${{ matrix.python-version}}
MKL_VERSION: ${{ matrix.mkl-version }}
FREE_THREADED: ${{ matrix.free-threaded && 'true' || 'false' }}
DO_COVERAGE: ${{ matrix.coverage && 'true' || 'false' }}
ENV_NAME: pydiso-ci

- name: Setup Conda
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
environment-file: environment_ci.yml
activate-environment: pydiso-ci

- name: Conda information
run: |
conda info
conda list
conda config --show

- name: Install Our Package
run: |
python -m pip install --no-build-isolation --verbose --editable . \
--config-setting=compile-args=-v \
${{ matrix.coverage && '--config-settings=setup-args="-Db_coverage=true"' || ''}} \
${{ matrix.os == 'windows-latest' && '--config-settings=setup-args="-Dvsenv=true"' || ''}}
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Run Tests
run: |
${{ matrix.coverage && 'coverage run -m' || '' }} pytest -s -v
${{ matrix.coverage && 'coverage xml' || '' }}
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.13"

- name: Create Conda environment file
run: |
python -m pip install pyyaml
python .github/make_ci_environ.py
env:
PYTHON_VERSION: ${{ matrix.python-version}}
MKL_VERSION: ${{ matrix.mkl-version }}
FREE_THREADED: ${{ matrix.free-threaded && 'true' || 'false' }}
DO_COVERAGE: ${{ matrix.coverage && 'true' || 'false' }}
ENV_NAME: pydiso-ci

- name: Setup Conda
uses: conda-incubator/setup-miniconda@v4
with:
auto-update-conda: true
environment-file: environment_ci.yml
activate-environment: pydiso-ci

- name: Conda information
run: |
conda info
conda list
conda config --show

- name: Install Our Package
run: |
# --no-deps: deps come from the conda env above, as conda-forge
# would. Opt out of [tool.meson-python]'s unconditional
# limited-API target for 3.14t specifically (no stable ABI
# exists before 3.15) - checked by version, not just
# free-threaded, so a future 3.15t entry isn't caught too.
python -m pip install --no-build-isolation --no-deps --verbose --editable . \
--config-setting=compile-args=-v \
${{ matrix.coverage && '--config-settings=setup-args="-Db_coverage=true"' || ''}} \
${{ matrix.os == 'windows-latest' && '--config-settings=setup-args="-Dvsenv=true"' || ''}} \
${{ matrix.python-version == '3.14' && matrix.free-threaded && '--config-settings=setup-args="-Dpython.allow_limited_api=false"' || ''}}

- name: Run Tests
run: |
${{ matrix.coverage && 'coverage run -m' || '' }} pytest -s -v
${{ matrix.coverage && 'coverage xml' || '' }}

- name: Upload coverage
if: ${{ matrix.coverage }}
uses: codecov/codecov-action@v7
with:
verbose: true # optional (default = false)
token: ${{ secrets.CODECOV_TOKEN }} # required

- name: Upload coverage
if: ${{ matrix.coverage }}
uses: codecov/codecov-action@v4
with:
verbose: true # optional (default = false)
token: ${{ secrets.CODECOV_TOKEN }} # required
build-and-test-pypi-mkl:
name: Testing (Python ${{ matrix.python-version }}, on ${{ matrix.os }}, with PyPI MKL)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: False
matrix:
# macOS excluded: Intel's PyPI mkl-devel wheel (frozen at 2023.2)
# is missing a symlink CMake needs, so MKL fails to import. See README.
os: [ubuntu-latest, windows-latest]
python-version: ["3.12", "3.13", "3.14"]

build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Build sdist
run: pipx run build --sdist -Csetup-args="-Ddist_ignore_mkl_dep=true"

- uses: actions/upload-artifact@v4
- name: Set up Python
uses: actions/setup-python@v7
with:
name: cibw-sdist
path: dist/*.tar.gz

release:
needs: [
build_sdist
]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
defaults:
run:
shell: bash -l {0}
steps:
- uses: actions/download-artifact@v4
with:
# unpacks all CIBW artifacts into dist/
pattern: cibw-*
path: dist
merge-multiple: true
python-version: ${{ matrix.python-version }}

- name: Release to github
uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true
prerelease: false
- name: Install our package against PyPI MKL
run: |
python -m pip install --upgrade pip
python -m pip install --verbose ".[test]"

- name: Upload wheels to pypi
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
skip-existing: true
packages-dir: ./dist/
- name: Run Tests
run: pytest -s -v
Loading