diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4140839..be2f338 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,8 +14,11 @@ jobs: name: pre-commit-hooks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@v6 with: python-version: "3.10" - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 508d4a7..7b70df0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,12 +18,13 @@ jobs: source: ["conda-forge"] # os: ["ubuntu-latest"] # source: ["source"] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 + persist-credentials: false - name: Conda uses: conda-incubator/setup-miniconda@v3 with: diff --git a/.github/workflows/update_graphblas.yml b/.github/workflows/update_graphblas.yml index 153f1a7..8a0ad69 100644 --- a/.github/workflows/update_graphblas.yml +++ b/.github/workflows/update_graphblas.yml @@ -27,11 +27,12 @@ jobs: if: github.repository == 'GraphBLAS/python-suitesparse-graphblas' || github.repository == 'alugowski/python-suitesparse-graphblas' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 + persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.11" diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 20434c0..e47b0a4 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -34,9 +34,10 @@ jobs: name: Build SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 + persist-credentials: false - name: Build SDist run: pipx run build --sdist @@ -94,9 +95,10 @@ jobs: cibw_archs: "arm64" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 + persist-credentials: false # aarch64 Linux builds are cross-compiled on x86 runners using emulation # see https://cibuildwheel.readthedocs.io/en/stable/faq/#emulation @@ -207,11 +209,11 @@ jobs: if: github.repository == 'GraphBLAS/python-suitesparse-graphblas' && ((github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_dispatch' && github.event.inputs.upload_dest != 'No Upload')) steps: - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.x" - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: path: dist merge-multiple: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2a8fd94..aebc857 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,6 +28,8 @@ repos: - id: mixed-line-ending args: [--fix=lf] - id: trailing-whitespace + - id: name-tests-test + args: ["--pytest-test-first"] - repo: https://github.com/abravalheri/validate-pyproject rev: v0.24.1 hooks: @@ -39,16 +41,16 @@ repos: - id: autoflake args: [--in-place] - repo: https://github.com/pycqa/isort - rev: 6.0.1 + rev: 7.0.0 hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.20.0 + rev: v3.21.0 hooks: - id: pyupgrade - args: [--py39-plus] + args: [--py310-plus] - repo: https://github.com/psf/black-pre-commit-mirror - rev: 25.1.0 + rev: 25.9.0 hooks: - id: black - repo: https://github.com/PyCQA/flake8 @@ -61,7 +63,7 @@ repos: - flake8==7.3.0 - flake8-comprehensions==3.17.0 - flake8-bugbear==24.12.12 - # - flake8-simplify==0.21.0 + # - flake8-simplify==0.22.0 - repo: https://github.com/asottile/yesqa rev: v1.5.0 hooks: @@ -70,12 +72,12 @@ repos: # `pyroma` may help keep our package standards up to date if best practices change. # This is a "low value" check though and too slow to run as part of pre-commit. # - repo: https://github.com/regebro/pyroma - # rev: "4.2" + # rev: "5.0" # hooks: # - id: pyroma # args: [-n, "10", .] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.33.3 + rev: 0.34.1 hooks: - id: check-dependabot - id: check-github-workflows diff --git a/Dockerfile b/Dockerfile index fa101e5..f20e91f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_CONTAINER=python:3.9-slim-buster +ARG BASE_CONTAINER=python:3.10-slim-buster FROM ${BASE_CONTAINER} as suitesparse ENV DEBIAN_FRONTEND=noninteractive @@ -24,7 +24,7 @@ ENV PYTHONUNBUFFERED 1 COPY --from=suitesparse /usr/include/GraphBLAS.h /usr/local/include/ COPY --from=suitesparse /usr/lib/x86_64-linux-gnu/libgraphblas* /usr/lib/x86_64-linux-gnu/ -COPY --from=suitesparse /build/pycparser/utils/fake_libc_include/* /usr/local/lib/python3.9/site-packages/pycparser/utils/fake_libc_include/ +COPY --from=suitesparse /build/pycparser/utils/fake_libc_include/* /usr/local/lib/python3.10/site-packages/pycparser/utils/fake_libc_include/ RUN apt-get update && apt-get install -yq build-essential git RUN pip3 install numpy cffi pytest cython @@ -44,4 +44,4 @@ RUN apt-get -y --purge remove git python3-pip && apt-get clean FROM ${BASE_CONTAINER} COPY --from=suitesparse /usr/lib/x86_64-linux-gnu/libgraphblas* /usr/lib/x86_64-linux-gnu/ COPY --from=suitesparse /usr/lib/x86_64-linux-gnu/libgomp* /usr/lib/x86_64-linux-gnu/ -COPY --from=psg /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages +COPY --from=psg /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages diff --git a/build_graphblas_cffi.py b/build_graphblas_cffi.py index 7548102..94594e6 100644 --- a/build_graphblas_cffi.py +++ b/build_graphblas_cffi.py @@ -13,7 +13,7 @@ # GraphBLAS_ROOT env var can point to the root directory of GraphBLAS to link against. # Expected subdirectories: include/ (contains GraphBLAS.h), lib/, and bin/ (on Windows only) # Otherwise fallback to default system folders. -graphblas_root = os.environ.get("GraphBLAS_ROOT", None) +graphblas_root = os.environ.get("GraphBLAS_ROOT") if not graphblas_root: # Windows wheels.yml configures suitesparse.sh to install GraphBLAS to "C:\\GraphBLAS". diff --git a/pyproject.toml b/pyproject.toml index 3a47125..5442f49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,9 @@ requires = [ "setuptools-git-versioning", "wheel", "cffi>=1.11", - "cython", + # Free-threading in Python 3.14 requires Cython >=3.1 + "cython; python_version<'3.14'", + "cython>=3.1; python_version>='3.14'", "numpy>=2.0", ] @@ -15,7 +17,7 @@ name = "suitesparse-graphblas" dynamic = ["version"] description = "SuiteSparse:GraphBLAS Python bindings." readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = {file = "LICENSE"} authors = [ {name = "Erik Welch", email = "erik.n.welch@gmail.com"}, @@ -51,12 +53,13 @@ classifiers = [ "Operating System :: Microsoft :: Windows", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: Free Threading :: 2 - Beta", "Intended Audience :: Developers", "Intended Audience :: Other Audience", "Intended Audience :: Science/Research", @@ -93,7 +96,7 @@ dirty_template = "{tag}+{ccount}.g{sha}.dirty" [tool.black] line-length = 100 -target-version = ["py39", "py310", "py311", "py312", "py313"] +target-version = ["py310", "py311", "py312", "py313", "py314"] [tool.isort] sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] diff --git a/suitesparse_graphblas/utils.pyx b/suitesparse_graphblas/utils.pyx index 95d2f1b..5c9ae99 100644 --- a/suitesparse_graphblas/utils.pyx +++ b/suitesparse_graphblas/utils.pyx @@ -1,3 +1,9 @@ +# cython: freethreading_compatible=True +# +# We don't do anything special to support free-threading, but GraphBLAS C +# libraries are required to be thread-safe, so things should "just work". +# Of course, users writing multithreaded code can find many creative ways +# to fail, but python-suitesparse-graphblas shouldn't crash. import numpy as np from cpython.ref cimport Py_INCREF from libc.stdint cimport uintptr_t