diff --git a/.github/workflows/wheel.yml b/.github/workflows/wheel.yml index 054503089..786e2c644 100644 --- a/.github/workflows/wheel.yml +++ b/.github/workflows/wheel.yml @@ -22,62 +22,17 @@ jobs: matrix: include: # linux-64 - - os: ubuntu-latest - python: 37 - platform_id: manylinux_x86_64 - - os: ubuntu-latest - python: 38 - platform_id: manylinux_x86_64 - - os: ubuntu-latest - python: 39 - platform_id: manylinux_x86_64 - - os: ubuntu-latest - python: 310 - platform_id: manylinux_x86_64 - os: ubuntu-latest python: 311 platform_id: manylinux_x86_64 - - os: ubuntu-latest - python: 312 - platform_id: manylinux_x86_64 # macos-x86-64 - - os: macos-latest - python: 37 - platform_id: macosx_x86_64 - - os: macos-latest - python: 38 - platform_id: macosx_universal2 - - os: macos-latest - python: 39 - platform_id: macosx_universal2 - - os: macos-latest - python: 310 - platform_id: macosx_universal2 - os: macos-latest python: 311 platform_id: macosx_universal2 - - os: macos-latest - python: 312 - platform_id: macosx_universal2 # win-64 - - os: windows-2019 - python: 37 - platform_id: win_amd64 - - os: windows-2019 - python: 38 - platform_id: win_amd64 - - os: windows-2019 - python: 39 - platform_id: win_amd64 - - os: windows-2019 - python: 310 - platform_id: win_amd64 - os: windows-2019 python: 311 platform_id: win_amd64 - - os: windows-2019 - python: 312 - platform_id: win_amd64 steps: - uses: actions/checkout@v4 - name: Build wheels @@ -99,7 +54,7 @@ jobs: - uses: actions/setup-python@v5 name: Install Python with: - python-version: '3.10' + python-version: '3.11' - run: python -m pip install build - name: Build sdist run: python -m build --sdist diff --git a/pyproject.toml b/pyproject.toml index 0479e6617..df4b7026c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ requires = [ "numpy>=2.0.0rc0; python_version >= '3.9'", "nodejs-wheel~=20.9", "pyyaml", - "cython>=0.16", + "cython>=3.0.1", ] build-backend = "setuptools.build_meta" @@ -120,6 +120,17 @@ test-skip = "cp37-*" [tool.cibuildwheel.linux] environment-pass = ["CIBW_BUILD"] +# Use abi3audit to catch issues with Limited API wheels +repair-wheel-command = [ + "auditwheel repair -w {dest_dir} {wheel}", + "pipx run abi3audit --strict --report {wheel}", +] + +[tool.cibuildwheel.macos] +repair-wheel-command = [ + "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}", + "pipx run abi3audit --strict --report {wheel}", +] [tool.pyright] include = [ diff --git a/reacnetgenerator/dps.pyx b/reacnetgenerator/dps.pyx index c6bbdfa29..3ef58ebf7 100644 --- a/reacnetgenerator/dps.pyx +++ b/reacnetgenerator/dps.pyx @@ -6,6 +6,8 @@ """Connect molecule with Depth-First Search.""" from libc.stdlib cimport free, malloc +import cython + cdef extern from "c_stack.h": # This function is copied from https://zhuanlan.zhihu.com/p/38212302 @@ -14,6 +16,7 @@ cdef extern from "c_stack.h": int pop() +@cython.binding(False) def dps(bonds, levels): """Connect molecule with Depth-First Search. @@ -35,7 +38,7 @@ def dps(bonds, levels): bondlist = [] cdef int _N = len(bonds) cdef int *visited = malloc(_N * sizeof(int)) - cdef int i, s, b_c, l + cdef int i, s, b_c, l, ib, nb cdef C_Stack st for i in range(_N): visited[i]=0 @@ -52,7 +55,10 @@ def dps(bonds, levels): elif visited[s]==1: continue mol.append(s) - for b, l in zip(bonds[s], levels[s]): + nb = len(bonds[s]) + for ib in range(nb): + b = bonds[s][nb] + l = levels[s][nb] b_c = b if visited[b_c]==0: # bond.append((s, b, l) if i < b else (b, s, l)) @@ -67,6 +73,7 @@ def dps(bonds, levels): return molecule, bondlist +@cython.binding(False) def dps_reaction(reactdict): """Find A+B->C+D reactions. @@ -87,8 +94,11 @@ def dps_reaction(reactdict): cdef set visited_right = set() visited = [visited_left, visited_right] cdef C_Stack st + cdef int nm, im, nr, ir - for init_mol in reactdict[0]: + nm = len(reactdict[0]) + for im in range(nm): + init_mol = reactdict[0][im] if init_mol not in visited[0]: reaction = [[], []] st.push(init_mol) @@ -100,7 +110,9 @@ def dps_reaction(reactdict): elif mol in visited[side]: continue reaction[side].append(mol) - for r in reactdict[side][mol]: + nr = len(reactdict[side][mol]) + for ir in range(nr): + r = reactdict[side][mol][ir] if r < 0: if r not in reaction[1-side]: reaction[1-side].append(r) diff --git a/reacnetgenerator/utils_np.pyx b/reacnetgenerator/utils_np.pyx index f0357beeb..7779bf872 100644 --- a/reacnetgenerator/utils_np.pyx +++ b/reacnetgenerator/utils_np.pyx @@ -4,6 +4,7 @@ # cython: linetrace=True # cython: infer_types=True +import cython import numpy as np cimport cython @@ -15,8 +16,7 @@ DTYPE8 = np.int8 ctypedef np.int8_t DTYPE8_t -@cython.boundscheck(False) -@cython.wraparound(False) +@cython.binding(False) cpdef idx_to_signal(DTYPE_t[:] idx, int step): """Converts an index array to a signal array. @@ -51,8 +51,7 @@ cpdef idx_to_signal(DTYPE_t[:] idx, int step): return signal -@cython.boundscheck(False) -@cython.wraparound(False) +@cython.binding(False) cpdef check_zero_signal(DTYPE8_t[:] signal): """Check if the given signal contains only zeros. diff --git a/setup.py b/setup.py index 84adc32c6..d866cc5f9 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,7 @@ import setuptools.command.build_ext import yaml from setuptools import Extension, setup +from wheel.bdist_wheel import bdist_wheel log = logging.getLogger(__name__) @@ -64,8 +65,19 @@ def run(self): super().run() +class bdist_wheel_abi3(bdist_wheel): + def get_tag(self): + python, abi, plat = super().get_tag() + + if python.startswith("cp"): + # on CPython, our wheels are abi3 and compatible back to 3.7 + return "cp37", "abi3", plat + + return python, abi, plat + + if __name__ == "__main__": - define_macros = [] + define_macros = [("CYTHON_LIMITED_API", "1"), ("Py_LIMITED_API", "0x030b0000")] if os.environ.get("DEBUG", 0): define_macros.extend((("CYTHON_TRACE", "1"), ("CYTHON_TRACE_NOGIL", "1"))) @@ -86,7 +98,5 @@ def run(self): setup( ext_modules=ext_modules, - cmdclass={ - "build_ext": BuildExtCommand, - }, + cmdclass={"build_ext": BuildExtCommand, "bdist_wheel": bdist_wheel_abi3}, )