diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f8a8e9e..1d49f43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,14 +16,53 @@ jobs: rid: linux-x64 target: x86_64-unknown-linux-gnu ext: .so + python-version: '3.9' + - os: ubuntu-latest + rid: linux-x64 + target: x86_64-unknown-linux-gnu + ext: .so + python-version: '3.13t' + freethreaded: true + - os: ubuntu-latest + rid: linux-x64 + target: x86_64-unknown-linux-gnu + ext: .so + python-version: '3.14t' + freethreaded: true + - os: macos-latest + rid: universal2 + target: universal2-apple-darwin + ext: .dylib + python-version: '3.9' - os: macos-latest rid: universal2 target: universal2-apple-darwin ext: .dylib + python-version: '3.13t' + freethreaded: true + - os: macos-latest + rid: universal2 + target: universal2-apple-darwin + ext: .dylib + python-version: '3.14t' + freethreaded: true + - os: windows-latest + rid: win-x64 + target: x86_64-pc-windows-msvc + ext: .dll + python-version: '3.9' + - os: windows-latest + rid: win-x64 + target: x86_64-pc-windows-msvc + ext: .dll + python-version: '3.13t' + freethreaded: true - os: windows-latest rid: win-x64 target: x86_64-pc-windows-msvc ext: .dll + python-version: '3.14t' + freethreaded: true steps: - uses: actions/checkout@v4 @@ -75,7 +114,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: ${{ matrix.python-version }} - name: Copy README run: cp README.md native_fisher_py/ @@ -84,13 +123,13 @@ jobs: uses: PyO3/maturin-action@v1 with: command: build - args: --release --out dist --manifest-path native_fisher_py/Cargo.toml + args: --release --out dist --manifest-path native_fisher_py/Cargo.toml ${{ matrix.freethreaded && '--no-default-features' || '' }} target: ${{ matrix.target }} - name: Upload wheels uses: actions/upload-artifact@v4 with: - name: wheels-${{ matrix.os }} + name: wheels-${{ matrix.os }}-${{ matrix.python-version }} path: dist/*.whl publish: diff --git a/build.sh b/build.sh index 2368926..742977b 100755 --- a/build.sh +++ b/build.sh @@ -18,6 +18,11 @@ fi echo "Building for RID: $RID" +# Some networks (e.g. VPNs with broken IPv6 routing) advertise an IPv6 route to +# nuget.org that is actually black-holed, causing NuGet restores to hang for +# minutes before failing with NU1301. Force IPv4 to avoid this. +export DOTNET_SYSTEM_NET_DISABLEIPV6=1 + # 1. Build C# NativeAOT cd native/ThermoNativeReader dotnet publish -r $RID -c Release -p:PublishAot=true @@ -36,14 +41,27 @@ export THERMO_NATIVE_LIB=$(pwd)/python/native_fisher_py/$LIB_NAME # Check for maturin cp ../README.md README.md +MATURIN_ARGS="develop" +for arg in "$@"; do + case $arg in + --wheel) MATURIN_ARGS="build --release --out ../dist" ;; + # Free-threaded builds can't use the abi3 stable ABI, so this must be run + # with a free-threaded interpreter (e.g. python3.13t) active/first on PATH. + --freethreaded) MATURIN_ARGS="build --release --out ../dist --no-default-features" ;; + esac +done if command -v maturin >/dev/null 2>&1; then - maturin develop + maturin $MATURIN_ARGS elif command -v pipenv >/dev/null 2>&1 && pipenv run maturin --version >/dev/null 2>&1; then - pipenv run maturin develop + pipenv run maturin $MATURIN_ARGS else echo "Error: maturin not found. Please install it with 'pip install maturin' or 'pipenv install maturin'." exit 1 fi cd .. -echo "Build complete. native-fisher-py is installed in your current environment." +if [[ "$MATURIN_ARGS" == build* ]]; then + echo "Build complete. Wheel written to dist/." +else + echo "Build complete. native-fisher-py is installed in your current environment." +fi diff --git a/native_fisher_py/Cargo.toml b/native_fisher_py/Cargo.toml index 09b9d4a..fb0c823 100644 --- a/native_fisher_py/Cargo.toml +++ b/native_fisher_py/Cargo.toml @@ -9,5 +9,13 @@ name = "native_fisher_py_backend" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.27.0", features = ["abi3-py39"] } +pyo3 = "0.27.0" libloading = "0.8" + +# abi3 (the stable ABI) is not compatible with free-threaded Python builds, so +# it's exposed as an opt-out feature: the release workflow builds the default +# abi3 wheel normally, and builds version-specific cp313t/cp314t wheels with +# `--no-default-features`. +[features] +default = ["abi3"] +abi3 = ["pyo3/abi3-py39"] diff --git a/native_fisher_py/pyproject.toml b/native_fisher_py/pyproject.toml index 7a98f08..64e5921 100644 --- a/native_fisher_py/pyproject.toml +++ b/native_fisher_py/pyproject.toml @@ -11,6 +11,7 @@ classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", + "Programming Language :: Python :: Free Threading", ] license = { text = "MIT" } dynamic = ["version"]