Skip to content
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 0 additions & 2 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ branch = True
data_file = build/coverage/.coverage
source =
netgraph_core
omit =
python/netgraph_core/_docs.py

[report]
show_missing = True
Expand Down
78 changes: 78 additions & 0 deletions .github/ci-constraints.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Exact versions for CI's Python environments and for the coverage venv of `make cov`, so a new
# release of a tool cannot change a CI result without a change in this repository: the accepted
# development environment's versions, and for what only Linux, Windows or Python 3.11 installs,
# the version a resolver chose for those platforms (its marker says where it applies). pip itself
# is one of them: a job installs this pip (`pip install -U -c` this file, which also downgrades a
# newer one) before anything else. They reach a build environment only through the mechanism of
# the tool that fills it: pip's own isolated build takes --build-constraint or
# PIP_BUILD_CONSTRAINT (since pip 26.2 never -c or PIP_CONSTRAINT), `python -m build` takes
# PIP_CONSTRAINT, and its uv installer (the release wheels) UV_CONSTRAINT. NumPy 2.5 needs Python
# 3.12; Python 3.11 gets the last NumPy that supports it. Update deliberately.
build==1.5.0
certifi==2026.7.22
cfgv==3.5.0
charset-normalizer==3.5.1
cmake==4.4.2
colorlog==6.12.0
coverage==7.15.4
distlib==0.4.3
docutils==0.23
filelock==3.32.3
gcovr==8.6
id==1.6.1
identify==2.6.19
idna==3.19
iniconfig==2.3.0
jaraco.classes==3.4.0
jaraco.context==6.1.2
jaraco.functools==4.6.0
Jinja2==3.1.6
keyring==25.7.0
lxml==6.1.2
markdown-it-py==4.2.0
MarkupSafe==3.0.3
mdurl==0.1.2
more-itertools==11.1.0
nh3==0.3.6
ninja==1.13.0
nodeenv==1.10.0
packaging==26.3
pathspec==1.1.1
pip==26.2.1
platformdirs==4.11.3
pluggy==1.6.0
pre_commit==4.6.2
pybind11==3.1.0
pybind11-stubgen==3.0.0
Pygments==2.21.0
pyproject_hooks==1.2.0
pyright==1.1.401
pytest==9.1.1
pytest-cov==7.1.0
python-discovery==1.5.2
PyYAML==6.0.3
readme_renderer==45.0
requests==2.34.2
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==15.0.0
ruff==0.11.13
scikit_build_core==1.0.3
setuptools==84.0.0
twine==7.0.0
typing_extensions==4.16.0
urllib3==2.7.0
virtualenv==21.7.4
wheel==0.48.0
numpy==2.5.2; python_version >= "3.12"
numpy==2.4.4; python_version < "3.12"
backports.tarfile==1.2.0; python_version < "3.12"
importlib_metadata==9.0.1; python_version < "3.12"
zipp==4.1.1; python_version < "3.12"
cffi==2.1.1; sys_platform == "linux"
cryptography==50.0.2; sys_platform == "linux"
jeepney==0.9.0; sys_platform == "linux"
pycparser==3.11; sys_platform == "linux"
SecretStorage==3.5.0; sys_platform == "linux"
colorama==0.4.6; sys_platform == "win32"
pywin32-ctypes==0.2.3; sys_platform == "win32"
77 changes: 70 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ jobs:
steps:
- uses: actions/checkout@v4

# Install uv for faster dependency installation (required on macOS, bundled in manylinux)
# Install uv for faster dependency installation (required on macOS, bundled in manylinux).
# An exact version, like the Python tools of .github/ci-constraints.txt.
- uses: astral-sh/setup-uv@v4
with:
version: "0.11.16"

# Build wheels using cibuildwheel
- name: Build wheels
Expand All @@ -62,31 +65,86 @@ jobs:

# Use uv for faster dependency installation (10-100x faster than pip)
CIBW_BUILD_FRONTEND: "build[uv]"
# The pins of .github/ci-constraints.txt for what uv installs without constraints of
# cibuildwheel's own: the build requirements of every wheel (scikit-build-core and its
# dependencies; pybind11 is pinned in pyproject.toml) and the test requirements. uv
# reads UV_CONSTRAINT only when a call passes no -c, so cibuildwheel's own tools keep
# its pins. Linux builds in a container that holds the project at /project.
CIBW_ENVIRONMENT_LINUX: "UV_CONSTRAINT=/project/.github/ci-constraints.txt"
CIBW_ENVIRONMENT_MACOS: UV_CONSTRAINT="$GITHUB_WORKSPACE/.github/ci-constraints.txt"
CIBW_ENVIRONMENT_WINDOWS: UV_CONSTRAINT="$GITHUB_WORKSPACE/.github/ci-constraints.txt"

# Target architecture per matrix entry (native compilation, no QEMU)
CIBW_ARCHS: "${{ matrix.cibw_archs }}"
CIBW_TEST_COMMAND: 'python -c "import netgraph_core; print(netgraph_core.__version__)"'
# The Python suite on every built wheel (the shipped configuration, hooks off; the
# free-threaded cp314t wheels included): exactly the tests that need a hook skip.
# Windows runs pytest without dev/run-checks.sh, after the same check of the hook
# setting: the wheel lacks the private hooks.
CIBW_TEST_REQUIRES: "pytest==9.1.1"
CIBW_TEST_COMMAND: "PYTHON=python bash {project}/dev/run-checks.sh pytest off -p no:cacheprovider"
CIBW_TEST_COMMAND_WINDOWS: >-
python -c "import sys, _netgraph_core as m; sys.exit('the wheel carries the private hooks' if hasattr(m, '_max_flows_per_call') else 0)"
&& python -m pytest {project}/tests/py -p no:cacheprovider
CIBW_BEFORE_ALL_LINUX: "yum install -y libatomic || (apt-get update && apt-get install -y libatomic1) || apk add libatomic"

- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ matrix.cibw_archs }}
path: ./wheelhouse/*.whl

# build fills its isolated environment with a plain pip install, which PIP_CONSTRAINT reaches;
# the same variable pins pip itself and build.
build_sdist:
name: Build SDist
runs-on: ubuntu-latest
env:
PIP_CONSTRAINT: ${{ github.workspace }}/.github/ci-constraints.txt
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Build SDist
run: pipx run build --sdist
run: |
python -m pip install -U pip
python -m pip install build
python -m build --sdist

- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz

# An installation built from the source distribution, tested with the Python suite of the same
# commit. pip builds it in an isolated environment of its own, which since pip 26.2 takes only
# build constraints (PIP_BUILD_CONSTRAINT), never PIP_CONSTRAINT: both name the same file.
# PIP_CONSTRAINT pins the venv's pip itself and pytest.
test_sdist:
name: Test an installation built from the SDist
needs: [build_sdist]
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: actions/download-artifact@v4
with:
name: cibw-sdist
path: sdist
- name: Build and install from the SDist
env:
PIP_CONSTRAINT: ${{ github.workspace }}/.github/ci-constraints.txt
PIP_BUILD_CONSTRAINT: ${{ github.workspace }}/.github/ci-constraints.txt
run: |
python -m venv "$RUNNER_TEMP/sdist-venv"
"$RUNNER_TEMP/sdist-venv/bin/python" -m pip install -U pip
"$RUNNER_TEMP/sdist-venv/bin/python" -m pip install pytest
"$RUNNER_TEMP/sdist-venv/bin/python" -m pip install -v sdist/*.tar.gz
- name: Python tests against the installation
run: PYTHON="$RUNNER_TEMP/sdist-venv/bin/python" bash dev/run-checks.sh pytest off -p no:cacheprovider

test_wheels_centos_stream_9:
name: Test wheels (CentOS Stream 9, py${{ matrix.python-version }})
needs: [build_wheels]
Expand All @@ -100,6 +158,8 @@ jobs:
- name: Install Python
run: dnf install -y python${{ matrix.python-version }} python${{ matrix.python-version }}-pip

- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
pattern: cibw-wheels-ubuntu-*
Expand All @@ -108,13 +168,16 @@ jobs:

- name: Install and test wheel
run: |
python${{ matrix.python-version }} -m pip install --upgrade pip
python${{ matrix.python-version }} -m pip install netgraph-core --find-links wheelhouse/
python${{ matrix.python-version }} -c "import netgraph_core; print('CentOS Stream 9:', netgraph_core.__version__)"
python${{ matrix.python-version }} -m venv /tmp/wheel-venv
/tmp/wheel-venv/bin/python -m pip install --upgrade -c .github/ci-constraints.txt pip
/tmp/wheel-venv/bin/python -m pip install -c .github/ci-constraints.txt pytest numpy
# --no-index: the wheel built above, never a published one of the same version.
/tmp/wheel-venv/bin/python -m pip install --no-index --find-links wheelhouse/ netgraph-core
PYTHON=/tmp/wheel-venv/bin/python bash dev/run-checks.sh pytest off -p no:cacheprovider

publish_pypi:
name: Publish to PyPI
needs: [check_version, build_wheels, build_sdist, test_wheels_centos_stream_9]
needs: [check_version, build_wheels, build_sdist, test_sdist, test_wheels_centos_stream_9]
runs-on: ubuntu-latest
environment: pypi
permissions:
Expand Down
100 changes: 90 additions & 10 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
pull_request:
workflow_dispatch:

# Every job installs its Python tools with the pins of .github/ci-constraints.txt, pip itself
# first. An editable install builds the package in an isolated environment of pip's own, which
# since pip 26.2 takes only build constraints (--build-constraint), never -c: both name the same
# file.
jobs:
test:
name: Unit tests (${{ matrix.os }}, py${{ matrix.python-version }})
Expand All @@ -15,21 +19,41 @@ jobs:
matrix:
os: [ubuntu-22.04, macos-latest, windows-latest]
python-version: ["3.11", "3.12", "3.13", "3.14"]
include:
# Free-threaded CPython (no GIL): the module declares py::mod_gil_not_used().
- os: ubuntu-22.04
python-version: "3.14t"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# bash on every OS: PowerShell would not stop when the first command fails.
- name: Install deps
shell: bash
run: |
python -m pip install -U pip wheel
python -m pip install -e .[dev]
- name: Run CI checks (lint + C++/Python tests)
python -m pip install -U -c .github/ci-constraints.txt pip
python -m pip install -c .github/ci-constraints.txt --build-constraint .github/ci-constraints.txt -e ".[dev]"
- name: Run CI checks (lint + C++ tests with and without the hooks + Python tests)
if: runner.os != 'Windows'
run: make check-ci
# Windows runs pytest without dev/run-checks.sh, so this step checks the hook setting that
# script would: the editable build carries the private hooks (no test then skips). A step of
# its own: PowerShell does not stop at a failing command inside one step.
- name: Check the editable build carries the private hooks (Windows)
if: runner.os == 'Windows'
run: python -c "import sys, _netgraph_core as m; sys.exit(0 if hasattr(m, '_max_flows_per_call') else 'the editable build lacks the private hooks')"
- name: Run Python tests (Windows)
if: runner.os == 'Windows'
run: python -m pytest tests/py/ -v
# The timing tool of dev/perf compiles against the library (its adapter uses the public
# API and two private functions of the queue rule), and nothing else builds it: its own
# tests, which compile its driver with this checkout's library, run on one job, so a
# library change that breaks the tool fails here. macOS: the platform the tool is made
# and checked on (its build record and memory reading are macOS-first).
- name: Timing tool tests (macOS, py3.14)
if: runner.os == 'macOS' && matrix.python-version == '3.14'
run: make perf-tool-test

test-centos-stream-9:
name: CentOS Stream 9 (py${{ matrix.python-version }})
Expand All @@ -53,12 +77,71 @@ jobs:

- name: Install Python deps
run: |
python${{ matrix.python-version }} -m pip install -U pip wheel
python${{ matrix.python-version }} -m pip install -e .[dev]
python${{ matrix.python-version }} -m pip install -U -c .github/ci-constraints.txt pip
python${{ matrix.python-version }} -m pip install -c .github/ci-constraints.txt \
--build-constraint .github/ci-constraints.txt -e ".[dev]"

- name: Run CI checks (lint + C++/Python tests)
- name: Run CI checks (lint + C++ tests with and without the hooks + Python tests)
run: PYTHON=python${{ matrix.python-version }} make check-ci

# The C++ tests with MSVC: a Release build with CMake's link-time optimisation (the wheel's
# configuration), with and without the private hooks.
cpp-windows-msvc:
name: C++ tests (windows, MSVC)
runs-on: windows-latest
defaults:
run:
shell: bash
env:
CC: cl
CXX: cl
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build tools
run: |
python -m pip install -U -c .github/ci-constraints.txt pip
python -m pip install -c .github/ci-constraints.txt cmake ninja
- uses: ilammy/msvc-dev-cmd@v1
- name: C++ tests, private hooks on
run: bash dev/cpp-tests.sh release
- name: C++ tests, private hooks off
run: bash dev/cpp-tests.sh off

sanitizers:
name: C++ tests (ubuntu, ASan + UBSan)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build tools
run: |
python -m pip install -U -c .github/ci-constraints.txt pip
python -m pip install -c .github/ci-constraints.txt cmake ninja
- name: C++ tests under AddressSanitizer and UndefinedBehaviorSanitizer
run: bash dev/cpp-tests.sh sanitize

# The generated type stub python/netgraph_core/__init__.pyi (make stubs-check): no drift from a
# fresh generation out of a wheel of this commit, the stub and py.typed in the wheel and the
# sdist, and the typed clients of tests/typing (valid: no diagnostic; invalid: exactly the
# expected ones) under pinned Pyright against the installed wheel and an installation built
# from the sdist. The script makes its own pinned venvs. Python 3.14: the version the
# committed stub is generated with.
stubs:
name: Type stub and typed clients (ubuntu, py3.14)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.14"
- name: Stub drift, package contents, typed clients (wheel and sdist)
run: PYTHON=python bash dev/stubs/run.sh check

coverage:
name: Coverage (ubuntu, py3.11)
runs-on: ubuntu-22.04
Expand All @@ -67,10 +150,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install deps
run: |
python -m pip install -U pip wheel
python -m pip install -e .[dev]
# make cov builds its own pinned venv (build/coverage-venv) and fails on any failure.
- name: Generate coverage (Python + C++)
run: make cov
- name: Upload coverage artifacts
Expand Down
Loading
Loading