Skip to content

ENH: abi3 #251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jan 16, 2025
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
13 changes: 13 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[run]
branch = True
source = sleepecg
omit =
*/examples/*
*/setup.py

[report]
exclude_lines =
pragma: no cover
if __name__ == .__main__.:
@abstractmethod
@abstractclassmethod
153 changes: 153 additions & 0 deletions .github/workflows/cibuildwheel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
name: Test
concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }}
cancel-in-progress: true

on:
pull_request:
paths-ignore:
- 'docs/**'
push:
branches:
- main
release:
types:
- published

jobs:
style:
name: Check style
runs-on: ubuntu-22.04
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: pre-commit/action@v3.0.1

build_wheels:
needs: style
strategy:
matrix:
os: [ubuntu-22.04, windows-2022, macos-15, macos-13]
arch: [native]
include:
- os: ubuntu-22.04
arch: aarch64
fail-fast: false
timeout-minutes: 15 # Linux ~1 min, Windows ~4 min, aarch64 emulated ~8 min with tests (~3 min without)
name: cibuildwheel (${{ matrix.os }} ${{ matrix.arch }})
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set wheel tests to be skipped on emulated archs on PRs
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]] && [[ "${{ matrix.arch }}" == "aarch64" ]]; then
echo "CIBW_TEST_SKIP=*" | tee -a $GITHUB_ENV
fi
shell: bash
# For aarch64 support https://cibuildwheel.pypa.io/en/stable/faq/#emulation
- uses: docker/setup-qemu-action@v3
with:
platforms: all
if: runner.os == 'Linux' && matrix.arch == 'aarch64'
- name: Build wheels and run tests
uses: pypa/cibuildwheel@v2.22.0
env:
CIBW_ARCHS: ${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: artifact-${{ matrix.os }}-${{ matrix.arch }}
path: ./wheelhouse/*.whl

test:
needs: build_wheels
strategy:
matrix:
os: [ubuntu-22.04, windows-2022, macos-15, macos-13]
python-version: ['3.12']
include:
- os: ubuntu-22.04
python-version: '3.13'
fail-fast: false
name: Test wheels (${{ matrix.os }} py${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
timeout-minutes: 5
defaults:
run:
shell: bash -eo pipefail {0}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: actions/download-artifact@v4
with:
pattern: artifact-${{ matrix.os }}-native
merge-multiple: true
path: dist
- run: ls -al dist
- name: Triage dependencies
run: |
if [[ "${{ matrix.python-version }}" != "3.13" ]]; then
echo "PIP_EXTRA=wfdb numpy<2 numba pytest-error-for-skips" | tee -a $GITHUB_ENV
echo "PYTEST_EXTRA=--error-for-skips" | tee -a $GITHUB_ENV
fi
- run: python -m pip install ./dist/*.whl pytest pytest-cov edfio $PIP_EXTRA --only-binary="numpy,numba,edfio"
- run: pytest -rfEXs --cov=sleepecg --cov-report=xml --tb=short --cov-branch --color=yes $PYTEST_EXTRA tests/
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
if: success() || failure()

build_sdist:
needs: style
name: Build source distribution
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
name: Install Python
with:
python-version: 3.12
- name: Build sdist
run: |
set -eo pipefail
python -m pip install build numpy
python -m build --sdist
- uses: actions/upload-artifact@v4
with:
name: artifact-sdist
path: dist/*.tar.gz

check_wheels:
needs: [build_wheels, build_sdist]
name: Check wheels and source distribution
runs-on: ubuntu-22.04
steps:
- uses: actions/download-artifact@v4
with:
pattern: artifact-*
merge-multiple: true
path: dist
- run: ls -al dist
shell: bash
- uses: actions/setup-python@v5
with:
python-version: 3.12
- run: python -m pip install twine
- run: python -m twine check --strict dist/*

upload-pypi:
name: Upload to PyPI
needs: [check_wheels, test]
runs-on: ubuntu-latest
if: github.event_name == 'release'
steps:
- uses: actions/download-artifact@v4
with:
pattern: artifact-*
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@v1.12.3
63 changes: 0 additions & 63 deletions .github/workflows/release.yml

This file was deleted.

33 changes: 0 additions & 33 deletions .github/workflows/test.yml

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
__pycache__/
site/
sleepecg.egg-info/
/build
/coverage.xml
/.coverage
25 changes: 25 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
comment: false
github_checks: # too noisy, even though "a" interactively disables them
annotations: false

codecov:
notify:
require_ci_to_pass: false

coverage:
status:
patch:
default:
informational: true
target: 95%
if_no_uploads: error
if_not_found: success
if_ci_failed: failure
project:
default: false
library:
informational: true
target: 90%
if_no_uploads: error
if_not_found: success
if_ci_failed: failure
20 changes: 17 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools", "setuptools-scm", "numpy"]
requires = ["setuptools", "setuptools-scm", "wheel", "numpy>=2,<3"]
build-backend = "setuptools.build_meta"

[project]
Expand All @@ -11,7 +11,7 @@ authors = [
{name = "Clemens Brunner", email = "clemens.brunner@gmail.com"},
]
readme = "README.md"
requires-python = ">= 3.9, < 3.13"
requires-python = ">= 3.9"
classifiers = [
"License :: OSI Approved :: BSD License",
"Operating System :: Microsoft :: Windows",
Expand All @@ -27,7 +27,7 @@ classifiers = [
]
keywords = ["sleep", "ecg", "qrs", "peak"]
dependencies = [
"numpy >= 1.25.0, < 2.0.0",
"numpy >= 1.25.0, < 3.0.0",
"PyYAML >= 5.4.0",
"requests >= 2.25.0",
"scipy >= 1.7.0",
Expand Down Expand Up @@ -64,6 +64,8 @@ cibw = [ # cibuildwheel uses this for running the test suite
"edfio >= 0.4.0",
"numba >= 0.59.1",
"wfdb >= 3.4.0",
# https://github.com/MIT-LCP/wfdb-python/pull/511
"numpy < 2.0.0", # TODO: just for wfdb!
]

[project.urls]
Expand All @@ -77,6 +79,14 @@ test-requires = "pytest"
test-extras = "cibw"
test-command = "pytest {package}"
skip = 'pp* *musllinux*'
build = "cp39-*" # abi3

[[tool.cibuildwheel.overrides]]
select = "*"
inherit.repair-wheel-command = "append"
repair-wheel-command = [
"pipx run abi3audit --strict --report {wheel}",
]

[tool.cibuildwheel.linux]
manylinux-x86_64-image = "manylinux2014"
Expand All @@ -87,6 +97,10 @@ archs = "x86_64 arm64"

[tool.cibuildwheel.windows]
archs = "AMD64"
before-build = "pip install delvewheel"
repair-wheel-command = [
"delvewheel repair -w {dest_dir} {wheel}",
]

[tool.mypy]
ignore_missing_imports = true
Expand Down
22 changes: 22 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
import numpy as np
from setuptools import Extension, setup
from wheel.bdist_wheel import bdist_wheel

# Following https://github.com/joerick/python-abi3-package-sample


# Currently set to 3.9. If this is bumped for example to 3.10, the hex
# in the Extension needs to change to 0x030A0000, and the cibuildwheel `build`
# selector needs to change in pyproject.toml

class bdist_wheel_abi3(bdist_wheel):
def get_tag(self) -> tuple:
python, abi, plat = super().get_tag()

if python.startswith("cp"):
# on CPython, our wheels are abi3 and compatible back to 3.6
return "cp39", "abi3", plat

return python, abi, plat


setup(
ext_modules=[
Extension(
"sleepecg._heartbeat_detection",
["src/sleepecg/_heartbeat_detection.c"],
include_dirs=[np.get_include()],
define_macros=[("Py_LIMITED_API", "0x03090000")],
py_limited_api=True,
),
],
cmdclass={"bdist_wheel": bdist_wheel_abi3},
)
Loading