From 590bc2feb8b748fa1211b2f90662a502dcab8529 Mon Sep 17 00:00:00 2001 From: Pravin Kamble Date: Sun, 27 Sep 2026 17:43:28 +0530 Subject: [PATCH 1/2] Add support python 3.15 --- .audit/python315.md | 10 ++ .github/templates/release-development.md.j2 | 2 +- .github/templates/release-nightly.md.j2 | 2 +- .github/templates/release-stable.md.j2 | 2 +- .github/workflows/README.md | 25 ++-- .github/workflows/main.yml | 50 +++---- .github/workflows/release.yml | 127 +++++++++++++++--- .github/workflows/wheels-arm64.yml | 19 +++ .github/workflows/wheels-docker.yml | 4 +- .github/workflows/wstest.yml | 48 +++---- README.md | 6 +- docs/changelog.rst | 1 + docs/installation.rst | 4 +- docs/websocket/conformance.rst | 8 +- docs/wheels-inventory.rst | 29 +++- .../serdes/tests/test_cbor_cross_version.py | 2 +- justfile | 38 +++--- pyproject.toml | 1 + 18 files changed, 269 insertions(+), 109 deletions(-) create mode 100644 .audit/python315.md diff --git a/.audit/python315.md b/.audit/python315.md new file mode 100644 index 000000000..ce6b83bf2 --- /dev/null +++ b/.audit/python315.md @@ -0,0 +1,10 @@ +## AI Assistance Disclosure + +- [ ] I did **not** use any AI-assistance tools to help create this pull request. +- [x] I **did** use AI-assistance tools to *help* create this pull request. +- [x] I have read, understood and followed the projects' [AI Policy](https://github.com/crossbario/autobahn-python/blob/main/AI_POLICY.md) when creating code, documentation etc. for this pull request. + +Submitted by: @p-r-a-v-i-n +Date: 2026-09-27 +Related issue(s): +Branch: p-r-a-v-i-n:python315 diff --git a/.github/templates/release-development.md.j2 b/.github/templates/release-development.md.j2 index f9c7a2a11..69b24dddd 100644 --- a/.github/templates/release-development.md.j2 +++ b/.github/templates/release-development.md.j2 @@ -16,7 +16,7 @@ Development build from pull request ## Python Versions -- **CPython**: 3.11, 3.12, 3.13, 3.14 +- **CPython**: 3.11, 3.12, 3.13, 3.14, 3.15 - **PyPy**: 3.11 ## Installation diff --git a/.github/templates/release-nightly.md.j2 b/.github/templates/release-nightly.md.j2 index 9905abef8..e692e82de 100644 --- a/.github/templates/release-nightly.md.j2 +++ b/.github/templates/release-nightly.md.j2 @@ -16,7 +16,7 @@ Automated nightly build from master branch ## Python Versions -- **CPython**: 3.11, 3.12, 3.13, 3.14 +- **CPython**: 3.11, 3.12, 3.13, 3.14, 3.15 - **PyPy**: 3.11 ## Installation diff --git a/.github/templates/release-stable.md.j2 b/.github/templates/release-stable.md.j2 index 6f0def5de..7f6c5cc7b 100644 --- a/.github/templates/release-stable.md.j2 +++ b/.github/templates/release-stable.md.j2 @@ -10,7 +10,7 @@ Official release {{ release_name }} ## Python Versions -- **CPython**: 3.11, 3.12, 3.13, 3.14 +- **CPython**: 3.11, 3.12, 3.13, 3.14, 3.15 - **PyPy**: 3.11 ## Installation diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 08c32153d..f2976d706 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -179,14 +179,18 @@ functionality to wheel installs including the flatc binary. |----------|--------------|----------|----------| | **main.yml** | `documentation` | `docs/_build/html/` | N/A | | **wstest.yml** | `wstest-results` | WebSocket conformance reports | N/A | -| **wheels.yml** | `wheels-macos-arm64` | macOS ARM64 wheels (cpy311-314, pypy311) | macOS arm64 | -| **wheels.yml** | `wheels-windows-x86_64` | Windows x64 wheels (cpy311-314, pypy311) | Windows x86_64 | -| **wheels.yml** | `wheels-windows-arm64` | Windows ARM64 wheels (cpy311-314, no PyPy) | Windows arm64 | +| **wheels.yml** | `wheels-macos-arm64` | macOS ARM64 wheels (cpy311-315, pypy311) | macOS arm64 | +| **wheels.yml** | `wheels-windows-x86_64` | Windows x64 wheels (cpy311-315, pypy311) | Windows x86_64 | +| **wheels.yml** | `wheels-windows-arm64` | Windows ARM64 wheels (cpy311-315, no PyPy) | Windows arm64 | | **wheels.yml** | `linux-wheels-no-nvx` | Pure Python wheels (no NVX) | Linux x86_64 | | **wheels.yml** | `source-distribution` | `*.tar.gz` sdist | Linux (build host) | | **wheels-docker.yml** | `artifacts-manylinux_2_28_x86_64` | Linux x64 wheels (see below) | Linux x86_64 | | **wheels-arm64.yml** | `artifacts-arm64-cpython-3.11-manylinux_2_28_aarch64` | CPython 3.11 wheel | Linux aarch64 | +| **wheels-arm64.yml** | `artifacts-arm64-cpython-3.12-manylinux_2_28_aarch64` | CPython 3.12 wheel | Linux aarch64 | | **wheels-arm64.yml** | `artifacts-arm64-cpython-3.13-manylinux_2_28_aarch64` | CPython 3.13 wheel | Linux aarch64 | +| **wheels-arm64.yml** | `artifacts-arm64-cpython-3.14-manylinux_2_28_aarch64` | CPython 3.14 wheel | Linux aarch64 | +| **wheels-arm64.yml** | `artifacts-arm64-cpython-3.15-manylinux_2_28_aarch64` | CPython 3.15 wheel | Linux aarch64 | +| **wheels-arm64.yml** | `artifacts-arm64-cpython-3.15-musllinux_1_2_aarch64` | CPython 3.15 musllinux wheel | Linux aarch64 | | **wheels-arm64.yml** | `artifacts-arm64-pypy-3.11-bookworm-manylinux_2_36_aarch64` | PyPy 3.11 wheel (Debian 12) | Linux aarch64 | | **wheels-arm64.yml** | `artifacts-arm64-pypy-3.11-trixie-manylinux_2_38_aarch64` | PyPy 3.11 wheel (Debian 13) | Linux aarch64 | @@ -195,6 +199,7 @@ functionality to wheel installs including the flatc binary. - `cpy312-linux-x86_64-manylinux_2_28` - `cpy313-linux-x86_64-manylinux_2_28` - `cpy314-linux-x86_64-manylinux_2_28` +- `cpy315-linux-x86_64-manylinux_2_28` - `pypy311-linux-x86_64-manylinux_2_28` ### 2. Artifact Consumer (release.yml) @@ -211,7 +216,11 @@ download action. It maps artifact names via the `check-workflows` job outputs: | `artifact_linux_no_nvx` | wheels.yml | `linux-wheels-no-nvx` | | `artifact_manylinux_x86_64` | wheels-docker.yml | `artifacts-manylinux_2_28_x86_64` | | `artifact_arm64_cp311` | wheels-arm64.yml | `artifacts-arm64-cpython-3.11-manylinux_2_28_aarch64` | +| `artifact_arm64_cp312` | wheels-arm64.yml | `artifacts-arm64-cpython-3.12-manylinux_2_28_aarch64` | | `artifact_arm64_cp313` | wheels-arm64.yml | `artifacts-arm64-cpython-3.13-manylinux_2_28_aarch64` | +| `artifact_arm64_cp314` | wheels-arm64.yml | `artifacts-arm64-cpython-3.14-manylinux_2_28_aarch64` | +| `artifact_arm64_cp315` | wheels-arm64.yml | `artifacts-arm64-cpython-3.15-manylinux_2_28_aarch64` | +| `artifact_arm64_cp315_musl` | wheels-arm64.yml | `artifacts-arm64-cpython-3.15-musllinux_1_2_aarch64` | | `artifact_arm64_pypy_bookworm` | wheels-arm64.yml | `artifacts-arm64-pypy-3.11-bookworm-manylinux_2_36_aarch64` | | `artifact_arm64_pypy_trixie` | wheels-arm64.yml | `artifacts-arm64-pypy-3.11-trixie-manylinux_2_38_aarch64` | @@ -221,12 +230,12 @@ download action. It maps artifact names via the `check-workflows` job outputs: | Platform | Architecture | Python Versions | Manylinux Tag | Workflow | |----------|--------------|-----------------|---------------|----------| -| Linux | x86_64 | 3.11, 3.12, 3.13, 3.14, PyPy 3.11 | manylinux_2_28 | wheels-docker.yml | -| Linux | aarch64 | 3.11, 3.13 | manylinux_2_28 | wheels-arm64.yml | +| Linux | x86_64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11 | manylinux_2_28 | wheels-docker.yml | +| Linux | aarch64 | 3.11, 3.12, 3.13, 3.14, 3.15 | manylinux_2_28 | wheels-arm64.yml | | Linux | aarch64 | PyPy 3.11 | manylinux_2_36/2_38 | wheels-arm64.yml | -| macOS | arm64 | 3.11, 3.12, 3.13, 3.14, PyPy 3.11 | N/A | wheels.yml | -| Windows | x86_64 | 3.11, 3.12, 3.13, 3.14, PyPy 3.11 | N/A | wheels.yml | -| Windows | arm64 | 3.11, 3.12, 3.13, 3.14 (no PyPy) | N/A | wheels.yml | +| macOS | arm64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11 | N/A | wheels.yml | +| Windows | x86_64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11 | N/A | wheels.yml | +| Windows | arm64 | 3.11, 3.12, 3.13, 3.14, 3.15 (no PyPy) | N/A | wheels.yml | ### Why Manylinux Containers? diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 625342102..64e7baa89 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -85,17 +85,17 @@ jobs: - name: Create Python environment and install tools run: | - just create cpy314 - just install-tools cpy314 + just create cpy315 + just install-tools cpy315 - name: Code formatting / linting (via ruff) - run: just check-format cpy314 + run: just check-format cpy315 - name: Code static typing (via ty) - run: just check-typing cpy314 + run: just check-typing cpy315 - name: Code test coverage (Combined Twisted + asyncio) - WITH NVX - run: just check-coverage-combined cpy314 1 + run: just check-coverage-combined cpy315 1 - name: Upload coverage report (with-nvx) if: always() @@ -106,7 +106,7 @@ jobs: retention-days: 14 - name: Code test coverage (Combined Twisted + asyncio) - WITHOUT NVX - run: just check-coverage-combined cpy314 0 + run: just check-coverage-combined cpy315 0 - name: Upload coverage report (without-nvx) if: always() @@ -117,16 +117,16 @@ jobs: retention-days: 14 - name: Unit test - Twisted / trial - WITH NVX - run: just test-twisted cpy314 1 + run: just test-twisted cpy315 1 - name: Unit test - Twisted / trial - WITHOUT NVX - run: just test-twisted cpy314 0 + run: just test-twisted cpy315 0 - name: Unit test - asyncio / pytest - WITH NVX - run: just test-asyncio cpy314 1 + run: just test-asyncio cpy315 1 - name: Unit test - asyncio / pytest - WITHOUT NVX - run: just test-asyncio cpy314 0 + run: just test-asyncio cpy315 0 test-serdes: name: SerDes Conformance Tests @@ -142,7 +142,7 @@ jobs: strategy: matrix: - python-env: [cpy314, cpy311, pypy311] + python-env: [cpy315, cpy311, pypy311] steps: - name: Checkout code @@ -230,12 +230,12 @@ jobs: PR_BRANCH: ${{ needs.identifiers.outputs.pr_branch }} # Runs on ALL supported Python versions (unlike the main test suite, which - # runs on cpy314 only) so import-time annotation regressions that only fire + # runs on cpy315 only) so import-time annotation regressions that only fire # on CPython < 3.14 - e.g. #1878 - are caught. Crypto extras are installed # (via install-dev -> autobahn[all]) so the cryptosign code paths run. strategy: matrix: - python-env: [cpy314, cpy313, cpy312, cpy311, pypy311] + python-env: [cpy315, cpy314, cpy313, cpy312, cpy311, pypy311] steps: - name: Checkout code @@ -393,11 +393,11 @@ jobs: - name: Create Python environment and install tools run: | - just create cpy314 - just install-tools cpy314 + just create cpy315 + just install-tools cpy315 - name: Build documentation - run: just docs cpy314 + run: just docs cpy315 - name: Upload documentation artifacts uses: wamp-proto/wamp-cicd/actions/upload-artifact-verified@main @@ -452,13 +452,13 @@ jobs: # - name: Create Python environment and install tools # run: | - # just create cpy314 - # just install-tools cpy314 + # just create cpy315 + # just install-tools cpy315 # - name: Run spellcheck # run: | # # Navigate to docs directory and run sphinx spellcheck - # cd docs && ../.venvs/cpy314/bin/sphinx-build -b spelling . _spelling + # cd docs && ../.venvs/cpy315/bin/sphinx-build -b spelling . _spelling # FIXME: 1 warning, 52 errors in 0.82s # @@ -500,12 +500,12 @@ jobs: # - name: Create Python environment and install package # run: | - # just create cpy314 - # just install-tools cpy314 - # just install-dev cpy314 + # just create cpy315 + # just install-tools cpy315 + # just install-dev cpy315 # - name: Run test suite - # run: just test cpy314 + # run: just test cpy315 build-schema: name: FlatBuffers Schema Build @@ -521,7 +521,7 @@ jobs: strategy: matrix: - python-env: [cpy314, cpy311, pypy311] + python-env: [cpy315, cpy311, pypy311] steps: - name: Checkout code @@ -684,7 +684,7 @@ jobs: strategy: matrix: - python-env: [cpy314, cpy311, pypy311] + python-env: [cpy315, cpy311, pypy311] steps: - name: Checkout code diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a7a33f8b4..c04eb2d63 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,10 +32,12 @@ jobs: artifact_arm64_cp312: ${{ steps.check.outputs.artifact_arm64_cp312 }} artifact_arm64_cp313: ${{ steps.check.outputs.artifact_arm64_cp313 }} artifact_arm64_cp314: ${{ steps.check.outputs.artifact_arm64_cp314 }} + artifact_arm64_cp315: ${{ steps.check.outputs.artifact_arm64_cp315 }} artifact_arm64_cp311_musl: ${{ steps.check.outputs.artifact_arm64_cp311_musl }} artifact_arm64_cp312_musl: ${{ steps.check.outputs.artifact_arm64_cp312_musl }} artifact_arm64_cp313_musl: ${{ steps.check.outputs.artifact_arm64_cp313_musl }} artifact_arm64_cp314_musl: ${{ steps.check.outputs.artifact_arm64_cp314_musl }} + artifact_arm64_cp315_musl: ${{ steps.check.outputs.artifact_arm64_cp315_musl }} artifact_arm64_pypy_bookworm: ${{ steps.check.outputs.artifact_arm64_pypy_bookworm }} artifact_arm64_pypy_trixie: ${{ steps.check.outputs.artifact_arm64_pypy_trixie }} @@ -155,10 +157,12 @@ jobs: core.setOutput('artifact_arm64_cp312', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.12-manylinux_2_28_aarch64')); core.setOutput('artifact_arm64_cp313', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.13-manylinux_2_28_aarch64')); core.setOutput('artifact_arm64_cp314', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.14-manylinux_2_28_aarch64')); + core.setOutput('artifact_arm64_cp315', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.15-manylinux_2_28_aarch64')); core.setOutput('artifact_arm64_cp311_musl', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.11-musllinux_1_2_aarch64')); core.setOutput('artifact_arm64_cp312_musl', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.12-musllinux_1_2_aarch64')); core.setOutput('artifact_arm64_cp313_musl', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.13-musllinux_1_2_aarch64')); core.setOutput('artifact_arm64_cp314_musl', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.14-musllinux_1_2_aarch64')); + core.setOutput('artifact_arm64_cp315_musl', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.15-musllinux_1_2_aarch64')); core.setOutput('artifact_arm64_pypy_bookworm', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-pypy-3.11-bookworm-manylinux_2_36_aarch64')); core.setOutput('artifact_arm64_pypy_trixie', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-pypy-3.11-trixie-manylinux_2_38_aarch64')); @@ -493,6 +497,17 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 CPython 3.15 artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_cp315 }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download and verify ARM64 CPython 3.11 musllinux artifacts with retry logic uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -537,6 +552,17 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 CPython 3.15 musllinux artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_cp315_musl }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download and verify ARM64 PyPy 3.11 Bookworm artifacts with retry logic uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -570,11 +596,11 @@ jobs: continue-on-error: true # Download FlatBuffers generated code (explicit enumeration for each matrix env) - - name: Download FlatBuffers generated code (cpy314) + - name: Download FlatBuffers generated code (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: - name: flatbuffers-gen-cpy314 - path: ${{ github.workspace }}/flatbuffers-gen/cpy314/ + name: flatbuffers-gen-cpy315 + path: ${{ github.workspace }}/flatbuffers-gen/cpy315/ run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true @@ -598,11 +624,11 @@ jobs: continue-on-error: true # Download FlatBuffers schema files (explicit enumeration for each matrix env) - - name: Download FlatBuffers schema files (cpy314) + - name: Download FlatBuffers schema files (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: - name: flatbuffers-schema-cpy314 - path: ${{ github.workspace }}/flatbuffers-schema/cpy314/ + name: flatbuffers-schema-cpy315 + path: ${{ github.workspace }}/flatbuffers-schema/cpy315/ run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true @@ -626,11 +652,11 @@ jobs: continue-on-error: true # Download SerDes conformance test results (explicit enumeration for each matrix env) - - name: Download SerDes test results (cpy314) + - name: Download SerDes test results (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: - name: serdes-test-results-cpy314 - path: ${{ github.workspace }}/serdes-test-results/cpy314/ + name: serdes-test-results-cpy315 + path: ${{ github.workspace }}/serdes-test-results/cpy315/ run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true @@ -945,6 +971,13 @@ jobs: cpy314-linux-aarch64-musllinux_1_2 cpy314-win-amd64 cpy314-win-arm64 + cpy315-macos-arm64 + cpy315-linux-x86_64-manylinux_2_28 + cpy315-linux-aarch64-manylinux_2_28 + cpy315-linux-x86_64-musllinux_1_2 + cpy315-linux-aarch64-musllinux_1_2 + cpy315-win-amd64 + cpy315-win-arm64 pypy311-macos-arm64 pypy311-linux-x86_64-manylinux_2_28 pypy311-linux-aarch64-manylinux_2_17 @@ -1426,6 +1459,17 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 CPython 3.15 artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_cp315 }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download and verify ARM64 CPython 3.11 musllinux artifacts with retry logic uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -1470,6 +1514,17 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 CPython 3.15 musllinux artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_cp315_musl }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download and verify ARM64 PyPy 3.11 Bookworm artifacts with retry logic uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -1503,11 +1558,11 @@ jobs: continue-on-error: true # Download FlatBuffers generated code (explicit enumeration for each matrix env) - - name: Download FlatBuffers generated code (cpy314) + - name: Download FlatBuffers generated code (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: - name: flatbuffers-gen-cpy314 - path: ${{ github.workspace }}/flatbuffers-gen/cpy314/ + name: flatbuffers-gen-cpy315 + path: ${{ github.workspace }}/flatbuffers-gen/cpy315/ run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true @@ -1531,11 +1586,11 @@ jobs: continue-on-error: true # Download FlatBuffers schema files (explicit enumeration for each matrix env) - - name: Download FlatBuffers schema files (cpy314) + - name: Download FlatBuffers schema files (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: - name: flatbuffers-schema-cpy314 - path: ${{ github.workspace }}/flatbuffers-schema/cpy314/ + name: flatbuffers-schema-cpy315 + path: ${{ github.workspace }}/flatbuffers-schema/cpy315/ run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true @@ -1559,11 +1614,11 @@ jobs: continue-on-error: true # Download SerDes conformance test results (explicit enumeration for each matrix env) - - name: Download SerDes test results (cpy314) + - name: Download SerDes test results (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: - name: serdes-test-results-cpy314 - path: ${{ github.workspace }}/serdes-test-results/cpy314/ + name: serdes-test-results-cpy315 + path: ${{ github.workspace }}/serdes-test-results/cpy315/ run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true @@ -1878,6 +1933,13 @@ jobs: cpy314-linux-aarch64-musllinux_1_2 cpy314-win-amd64 cpy314-win-arm64 + cpy315-macos-arm64 + cpy315-linux-x86_64-manylinux_2_28 + cpy315-linux-aarch64-manylinux_2_28 + cpy315-linux-x86_64-musllinux_1_2 + cpy315-linux-aarch64-musllinux_1_2 + cpy315-win-amd64 + cpy315-win-arm64 pypy311-macos-arm64 pypy311-linux-x86_64-manylinux_2_28 pypy311-linux-aarch64-manylinux_2_17 @@ -2379,6 +2441,17 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 CPython 3.15 artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_cp315 }} + path: ${{ github.workspace }}/dist/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download and verify ARM64 CPython 3.11 musllinux artifacts with retry logic uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -2423,6 +2496,17 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 CPython 3.15 musllinux artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_cp315_musl }} + path: ${{ github.workspace }}/dist/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download and verify ARM64 PyPy 3.11 Bookworm artifacts with retry logic uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -2602,6 +2686,13 @@ jobs: cpy314-linux-aarch64-musllinux_1_2 cpy314-win-amd64 cpy314-win-arm64 + cpy315-macos-arm64 + cpy315-linux-x86_64-manylinux_2_28 + cpy315-linux-aarch64-manylinux_2_28 + cpy315-linux-x86_64-musllinux_1_2 + cpy315-linux-aarch64-musllinux_1_2 + cpy315-win-amd64 + cpy315-win-arm64 pypy311-macos-arm64 pypy311-linux-x86_64-manylinux_2_28 pypy311-linux-aarch64-manylinux_2_17 diff --git a/.github/workflows/wheels-arm64.yml b/.github/workflows/wheels-arm64.yml index 127ed44e2..83d9584ad 100644 --- a/.github/workflows/wheels-arm64.yml +++ b/.github/workflows/wheels-arm64.yml @@ -89,6 +89,16 @@ jobs: build_type: "official" python_versions: "cpy314" + # CPython 3.15 - manylinux_2_28_aarch64 (glibc 2.28) + # Modern baseline (Debian 10+, Ubuntu 18.04+, RHEL 8+) + - name: "cpython-3.15-manylinux_2_28_aarch64" + base_image: "quay.io/pypa/manylinux_2_28_aarch64" + manylinux_tag: "manylinux_2_28_aarch64" + glibc_version: "2.28" + python_impl: "cpython" + build_type: "official" + python_versions: "cpy315" + # ============================================================ # CPython ARM64 musllinux wheels (Alpine / musl libc) — #1877 # Official PyPA musllinux_1_2 image; auditwheel auto-tags musllinux. @@ -130,6 +140,15 @@ jobs: build_type: "official" python_versions: "cpy314" + # CPython 3.15 - musllinux_1_2_aarch64 + - name: "cpython-3.15-musllinux_1_2_aarch64" + base_image: "quay.io/pypa/musllinux_1_2_aarch64" + manylinux_tag: "musllinux_1_2_aarch64" + glibc_version: "musl" + python_impl: "cpython" + build_type: "official" + python_versions: "cpy315" + # ============================================================ # PyPy ARM64 wheels (using our custom manylinux images) # ============================================================ diff --git a/.github/workflows/wheels-docker.yml b/.github/workflows/wheels-docker.yml index 6ece28977..848ff9586 100644 --- a/.github/workflows/wheels-docker.yml +++ b/.github/workflows/wheels-docker.yml @@ -57,10 +57,10 @@ jobs: # musllinux_1_2 (Alpine / musl libc) wheels — CPython only. # The pypa musllinux image ships no PyPy under /opt/python, so we build - # cpy311-314 explicitly (not `build-all`, which includes pypy311). #1877 + # cpy311-315 explicitly (not `build-all`, which includes pypy311). #1877 - name: "musllinux_1_2_x86_64" base_image: "quay.io/pypa/musllinux_1_2_x86_64" - build_envs: "cpy311 cpy312 cpy313 cpy314" + build_envs: "cpy311 cpy312 cpy313 cpy314 cpy315" steps: - name: Checkout code diff --git a/.github/workflows/wstest.yml b/.github/workflows/wstest.yml index 93f4927e6..ca7facdd7 100644 --- a/.github/workflows/wstest.yml +++ b/.github/workflows/wstest.yml @@ -77,7 +77,7 @@ jobs: Setup Python environments and install Autobahn|Python run: | just install cpy311 - just install cpy314 + just install cpy315 just install pypy311 - name: Run all client test combinations (with-nvx and without-nvx) @@ -127,13 +127,13 @@ jobs: echo "==> Testing asyncio + cpy311 client ($nvx_config)..." just wstest-testeeclient-asyncio cpy311 - # Client 3: Twisted + CPython 3.14 - echo "==> Testing Twisted + cpy314 client ($nvx_config)..." - just wstest-testeeclient-twisted cpy314 + # Client 3: Twisted + CPython 3.15 + echo "==> Testing Twisted + cpy315 client ($nvx_config)..." + just wstest-testeeclient-twisted cpy315 - # Client 4: asyncio + CPython 3.14 - echo "==> Testing asyncio + cpy314 client ($nvx_config)..." - just wstest-testeeclient-asyncio cpy314 + # Client 4: asyncio + CPython 3.15 + echo "==> Testing asyncio + cpy315 client ($nvx_config)..." + just wstest-testeeclient-asyncio cpy315 # Client 5: Twisted + PyPy 3.11 echo "==> Testing Twisted + pypy311 client ($nvx_config)..." @@ -294,7 +294,7 @@ jobs: Setup Python environments and install Autobahn|Python run: | just install cpy311 - just install cpy314 + just install cpy315 just install pypy311 - name: Run server tests (with-nvx and without-nvx) @@ -350,35 +350,35 @@ jobs: sleep 1 done - # Server 3: Twisted + CPython 3.14 on port 9013 - echo "==> Starting Twisted + cpy314 server on port 9013 ($nvx_config)..." - nohup .venvs/cpy314/bin/python ./wstest/testee_server_tx.py --url ws://127.0.0.1:9013 > twisted-cpy314-$nvx_config.log 2>&1 & - echo $! > twisted-cpy314.pid + # Server 3: Twisted + CPython 3.15 on port 9013 + echo "==> Starting Twisted + cpy315 server on port 9013 ($nvx_config)..." + nohup .venvs/cpy315/bin/python ./wstest/testee_server_tx.py --url ws://127.0.0.1:9013 > twisted-cpy315-$nvx_config.log 2>&1 & + echo $! > twisted-cpy315.pid for i in {1..30}; do if netstat -tuln | grep :9013 >/dev/null 2>&1; then - echo "✅ Twisted + cpy314 server running on port 9013" + echo "✅ Twisted + cpy315 server running on port 9013" break fi if [ $i -eq 30 ]; then - echo "❌ Twisted + cpy314 server failed to start on port 9013" - cat twisted-cpy314-$nvx_config.log + echo "❌ Twisted + cpy315 server failed to start on port 9013" + cat twisted-cpy315-$nvx_config.log exit 1 fi sleep 1 done - # Server 4: asyncio + CPython 3.14 on port 9014 - echo "==> Starting asyncio + cpy314 server on port 9014 ($nvx_config)..." - nohup .venvs/cpy314/bin/python ./wstest/testee_server_aio.py --url ws://127.0.0.1:9014 > asyncio-cpy314-$nvx_config.log 2>&1 & - echo $! > asyncio-cpy314.pid + # Server 4: asyncio + CPython 3.15 on port 9014 + echo "==> Starting asyncio + cpy315 server on port 9014 ($nvx_config)..." + nohup .venvs/cpy315/bin/python ./wstest/testee_server_aio.py --url ws://127.0.0.1:9014 > asyncio-cpy315-$nvx_config.log 2>&1 & + echo $! > asyncio-cpy315.pid for i in {1..30}; do if netstat -tuln | grep :9014 >/dev/null 2>&1; then - echo "✅ asyncio + cpy314 server running on port 9014" + echo "✅ asyncio + cpy315 server running on port 9014" break fi if [ $i -eq 30 ]; then - echo "❌ asyncio + cpy314 server failed to start on port 9014" - cat asyncio-cpy314-$nvx_config.log + echo "❌ asyncio + cpy315 server failed to start on port 9014" + cat asyncio-cpy315-$nvx_config.log exit 1 fi sleep 1 @@ -427,7 +427,7 @@ jobs: # Stop all servers for this configuration echo "==> Stopping all WebSocket servers for $nvx_config..." - for pid_file in twisted-cpy311.pid asyncio-cpy311.pid twisted-cpy314.pid asyncio-cpy314.pid twisted-pypy311.pid asyncio-pypy311.pid; do + for pid_file in twisted-cpy311.pid asyncio-cpy311.pid twisted-cpy315.pid asyncio-cpy315.pid twisted-pypy311.pid asyncio-pypy311.pid; do if [ -f "$pid_file" ]; then kill $(cat "$pid_file") 2>/dev/null || true rm -f "$pid_file" @@ -686,7 +686,7 @@ jobs: run: | echo "==> WebSocket Conformance Testing Complete ✅" echo "Test Mode: ${{ env.TEST_MODE }}" - echo "Matrix tested: (client|server) × (twisted|asyncio) × (cpy311|cpy314|pypy311) × (with-nvx|without-nvx)" + echo "Matrix tested: (client|server) × (twisted|asyncio) × (cpy311|cpy315|pypy311) × (with-nvx|without-nvx)" echo "" echo "Reports generated for both NVX configurations:" for nvx_config in "with-nvx" "without-nvx"; do diff --git a/README.md b/README.md index 433fe4416..b4d5f4410 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Autobahn|Python publishes source distributions and pre-built wheels on [PyPI](https://pypi.org/project/autobahn/) and [GitHub Releases](https://github.com/crossbario/autobahn-python/releases). The current release line requires Python 3.11 or later and publishes -wheels for CPython 3.11 through 3.14 and PyPy 3.11 across supported +wheels for CPython 3.11 through 3.15 and PyPy 3.11 across supported Linux, macOS, and Windows targets. The recommended installation method is: @@ -382,7 +382,7 @@ All serializer dependencies provide binary wheels for: - **Linux**: x86_64, ARM64 (manylinux, musllinux) - **macOS**: x86_64 (Intel), ARM64 (Apple Silicon) - **Windows**: x86_64 (AMD64), ARM64 -- **Python**: 3.11, 3.12, 3.13, 3.14 (including 3.14t free-threaded) +- **Python**: 3.11, 3.12, 3.13, 3.14, 3.15 (including 3.14t free-threaded) - **Implementations**: CPython, PyPy 3.11+ #### Backwards Compatibility @@ -503,7 +503,7 @@ Available via `pip install autobahn[nvx]`: **Binary wheels available for:** - **Operating Systems**: Linux (glibc/musl), macOS, Windows - **Architectures**: x86_64 (Intel/AMD), ARM64 (Apple Silicon, AWS Graviton) -- **Python Versions**: 3.11, 3.12, 3.13, 3.14 (including free-threaded 3.14t) +- **Python Versions**: 3.11, 3.12, 3.13, 3.14, 3.15 (including free-threaded 3.14t) - **Implementations**: CPython, PyPy 3.11+ **All optional dependencies install cleanly without:** diff --git a/docs/changelog.rst b/docs/changelog.rst index bed171a7b..b28e6eccd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -40,6 +40,7 @@ Changelog **Build & CI/CD** +* Add CPython 3.15 support across package metadata, CI, developer environments, binary wheel builds, release validation, and documentation. * Add CalVer / PEP 440 version-management ``just`` recipes (``file-version``, ``bump-dev``, ``bump-next``, ``prep-release``) mirroring Crossbar.io, and document the versioning policy in ``CONTRIBUTING.md`` (#1894) * Add ``ruff check --select ANN,UP,TCH`` (annotation presence, ``pyupgrade`` modern syntax, ``TYPE_CHECKING`` imports) to the ``just check-typing`` recipe so annotation/style regressions are caught in the ``quality-checks`` CI job. The existing gaps in ``src/autobahn/`` are ratcheted via an explicit ``--ignore`` allowlist to be removed module-by-module (#1839); all other ``UP``/``TC`` rules are enforced immediately, and generated code is excluded. The annotation rules are scoped to this recipe via the command line rather than the global ``[tool.ruff.lint]`` select, so the repo-wide ``check-format`` gate is unaffected (#1840) * Fix the aarch64 CPython 3.14 wheel shipping the free-threaded ABI (``cp314t``) in the GIL ``cp314`` slot (26.6.x). Root cause: manylinux images pre-install both the GIL and free-threaded 3.14 under ``/opt/python`` and prepend them to ``PATH``, and ``uv`` resolved ``cpython-3.14`` to the free-threaded interpreter (first on ``PATH``). The ``create`` recipe now drops free-threaded ``…t/bin`` dirs from ``PATH`` for GIL envs so ``uv`` selects the GIL build. As defence-in-depth, ``just build`` also asserts (via ``_check-venv-abi``) that the interpreter's GIL/free-threaded status matches the env and aborts on mismatch, so a wrong-ABI wheel can never be published. A reserved ``cpy314t`` env spec (``cpython-3.14t``) is added for a future free-threaded wheel variant (#1875) diff --git a/docs/installation.rst b/docs/installation.rst index 1488eef50..8d960c038 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -34,7 +34,7 @@ Here are the configurations supported by |ab|: - Twisted - asyncio - Notes - * - CPython 3.11, 3.12, 3.13, and 3.14 + * - CPython 3.11, 3.12, 3.13, 3.14, and 3.15 - yes - yes - Pre-built wheels are published for supported platforms. @@ -90,7 +90,7 @@ using `Pip `_: This is the recommended installation method. Pip will select a pre-built wheel when one matches your Python implementation, Python version, operating system, and CPU architecture. Current releases publish -wheels for CPython 3.11 through 3.14 and PyPy 3.11 across the supported +wheels for CPython 3.11 through 3.15 and PyPy 3.11 across the supported Linux, macOS, and Windows targets documented in :doc:`wheels-inventory`. You can also specify *install variants* (see below). E.g. to install Twisted automatically as a dependency diff --git a/docs/websocket/conformance.rst b/docs/websocket/conformance.rst index 308d57a6c..2287fbc7a 100644 --- a/docs/websocket/conformance.rst +++ b/docs/websocket/conformance.rst @@ -60,10 +60,10 @@ Client Testing # Terminal 2: Test Twisted & asyncio clients across Python versions just wstest-testeeclient-twisted cpy311 - just wstest-testeeclient-twisted cpy314 + just wstest-testeeclient-twisted cpy315 just wstest-testeeclient-twisted pypy311 just wstest-testeeclient-asyncio cpy311 - just wstest-testeeclient-asyncio cpy314 + just wstest-testeeclient-asyncio cpy315 just wstest-testeeclient-asyncio pypy311 Test results will be generated in the ``.wstest/`` directory: @@ -78,8 +78,8 @@ Server Testing (6 combinations) # Terminal 1-6: Start all server combinations just wstest-testeeserver-twisted cpy311 "ws://127.0.0.1:9011" just wstest-testeeserver-asyncio cpy311 "ws://127.0.0.1:9012" - just wstest-testeeserver-twisted cpy314 "ws://127.0.0.1:9013" - just wstest-testeeserver-asyncio cpy314 "ws://127.0.0.1:9014" + just wstest-testeeserver-twisted cpy315 "ws://127.0.0.1:9013" + just wstest-testeeserver-asyncio cpy315 "ws://127.0.0.1:9014" just wstest-testeeserver-twisted pypy311 "ws://127.0.0.1:9015" just wstest-testeeserver-asyncio pypy311 "ws://127.0.0.1:9016" diff --git a/docs/wheels-inventory.rst b/docs/wheels-inventory.rst index 9430513ae..01e2617af 100644 --- a/docs/wheels-inventory.rst +++ b/docs/wheels-inventory.rst @@ -58,6 +58,9 @@ Linux x86_64 * - CPython 3.14 - ``autobahn-{version}-cp314-cp314-linux_x86_64.whl`` - ❌ No (pure Python) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-linux_x86_64.whl`` + - ❌ No (pure Python) * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-linux_x86_64.whl`` - ❌ No (pure Python) @@ -88,6 +91,9 @@ Linux x86_64 (musllinux / Alpine) * - CPython 3.14 - ``autobahn-{version}-cp314-cp314-musllinux_1_2_x86_64.whl`` - ✅ Yes (binary) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-musllinux_1_2_x86_64.whl`` + - ✅ Yes (binary) **Note:** musllinux wheels cover CPython only. PyPy is not published for musllinux because the official PyPA musllinux images ship no PyPy interpreter (tracked as a follow-up). Without these @@ -118,6 +124,9 @@ macOS ARM64 (Apple Silicon) * - CPython 3.14 - ``autobahn-{version}-cp314-cp314-macosx_15_0_arm64.whl`` - ✅ Yes (binary) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-macosx_15_0_arm64.whl`` + - ✅ Yes (binary) * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-macosx_15_0_arm64.whl`` - ✅ Yes (binary) @@ -146,6 +155,9 @@ Windows x86_64 * - CPython 3.14 - ``autobahn-{version}-cp314-cp314-win_amd64.whl`` - ✅ Yes (binary) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-win_amd64.whl`` + - ✅ Yes (binary) * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-win_amd64.whl`` - ✅ Yes (binary) @@ -174,6 +186,9 @@ Windows ARM64 * - CPython 3.14 - ``autobahn-{version}-cp314-cp314-win_arm64.whl`` - ✅ Yes (binary) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-win_arm64.whl`` + - ✅ Yes (binary) **Note:** There is no PyPy wheel for Windows ARM64: the PyPy project publishes no Windows ARM64 interpreter. @@ -196,9 +211,18 @@ Linux ARM64 (CPython) * - CPython 3.11 - ``autobahn-{version}-cp311-cp311-manylinux_2_28_aarch64.whl`` - ✅ Yes (binary) + * - CPython 3.12 + - ``autobahn-{version}-cp312-cp312-manylinux_2_28_aarch64.whl`` + - ✅ Yes (binary) * - CPython 3.13 - ``autobahn-{version}-cp313-cp313-manylinux_2_28_aarch64.whl`` - ✅ Yes (binary) + * - CPython 3.14 + - ``autobahn-{version}-cp314-cp314-manylinux_2_28_aarch64.whl`` + - ✅ Yes (binary) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-manylinux_2_28_aarch64.whl`` + - ✅ Yes (binary) **Compatibility:** Requires glibc 2.28 or later (Debian 10+, Ubuntu 18.04+, RHEL 8+) @@ -226,6 +250,9 @@ Linux ARM64 (CPython, musllinux / Alpine) * - CPython 3.14 - ``autobahn-{version}-cp314-cp314-musllinux_1_2_aarch64.whl`` - ✅ Yes (binary) + * - CPython 3.15 + - ``autobahn-{version}-cp315-cp315-musllinux_1_2_aarch64.whl`` + - ✅ Yes (binary) **Compatibility:** musl libc 1.2 or later (Alpine 3.12+). CPython only; PyPy musllinux is a tracked follow-up (no official PyPA musllinux PyPy image). @@ -411,7 +438,7 @@ Common Tags **Python tags:** -* ``cp311``, ``cp312``, ``cp313``, ``cp314`` - CPython 3.11, 3.12, 3.13, 3.14 +* ``cp311``, ``cp312``, ``cp313``, ``cp314``, ``cp315`` - CPython 3.11, 3.12, 3.13, 3.14, 3.15 * ``pp311`` - PyPy 3.11 **Platform tags:** diff --git a/examples/serdes/tests/test_cbor_cross_version.py b/examples/serdes/tests/test_cbor_cross_version.py index 73e409aea..30ea1d906 100644 --- a/examples/serdes/tests/test_cbor_cross_version.py +++ b/examples/serdes/tests/test_cbor_cross_version.py @@ -11,7 +11,7 @@ For the WAMP wire protocol this is only safe if both implementations encode and decode CBOR *byte-for-byte identically*. These tests turn that requirement into an assertion that runs on BOTH runtimes in CI (the ``test-serdes`` matrix covers -``cpy311``/``cpy314`` and ``pypy311``): +``cpy311``/``cpy315`` and ``pypy311``): 1. ``test_cbor2_version_matches_runtime`` -- a provenance guard: fail loudly if the installed cbor2 is not the version the dependency markers intend for this diff --git a/justfile b/justfile index 2377099bc..55c72a47c 100644 --- a/justfile +++ b/justfile @@ -80,7 +80,7 @@ VENV_DIR := PROJECT_DIR / '.venvs' # Define a justfile-local variable for our environments. # PyPy publishes no Windows ARM64 interpreter, so win_arm64 is CPython-only. -ENVS := if os() + "-" + arch() == "windows-aarch64" { 'cpy314 cpy313 cpy312 cpy311' } else { 'cpy314 cpy313 cpy312 cpy311 pypy311' } +ENVS := if os() + "-" + arch() == "windows-aarch64" { 'cpy315 cpy314 cpy313 cpy312 cpy311' } else { 'cpy315 cpy314 cpy313 cpy312 cpy311 pypy311' } # Package version files, kept in sync (CalVer YY.M.PATCH[.devN], PEP 440). PY_VERSION_FILE := 'src/autobahn/_version.py' @@ -96,6 +96,7 @@ _get-spec short_name: #!/usr/bin/env bash set -e case {{short_name}} in + cpy315) echo "cpython-3.15{{PY_PLATFORM_SUFFIX}}";; cpy314) echo "cpython-3.14{{PY_PLATFORM_SUFFIX}}";; # cpython-3.14.0b3-linux-x86_64-gnu cpy314t) echo "cpython-3.14t{{PY_PLATFORM_SUFFIX}}";; # CPython 3.14 free-threaded (no-GIL); reserved for #1875 Part 2 cpy313) echo "cpython-3.13{{PY_PLATFORM_SUFFIX}}";; # cpython-3.13.5-linux-x86_64-gnu @@ -315,7 +316,7 @@ list-all: echo echo "Create a Python venv using: just create " -# Create a single Python virtual environment (usage: `just create cpy314` or `just create`) +# Create a single Python virtual environment (usage: `just create cpy315` or `just create`) create venv="": #!/usr/bin/env bash set -e @@ -380,7 +381,7 @@ create-all: just create ${venv} done -# Get the version of a single virtual environment's Python (usage: `just version cpy314`) +# Get the version of a single virtual environment's Python (usage: `just version cpy315`) version venv="": #!/usr/bin/env bash set -e @@ -532,7 +533,7 @@ link-system-packages venv="" vendors="": (create venv) # -- Installation and Test # ----------------------------------------------------------------------------- -# Install this package and its run-time dependencies in a single environment (usage: `just install cpy314` or `just install`) +# Install this package and its run-time dependencies in a single environment (usage: `just install cpy315` or `just install`) install venv="": (create venv) #!/usr/bin/env bash set -e @@ -548,7 +549,7 @@ install venv="": (create venv) # uv pip install --python "{{VENV_DIR}}/${VENV_NAME}/bin/python" .[all] ${VENV_PYTHON} -m pip install .[all] -# Install this package in development (editable) mode and its run-time dependencies in a single environment (usage: `just install-dev cpy314` or `just install-dev`) +# Install this package in development (editable) mode and its run-time dependencies in a single environment (usage: `just install-dev cpy315` or `just install-dev`) install-dev venv="": (create venv) #!/usr/bin/env bash set -e @@ -601,7 +602,7 @@ install-dev-local venv="": (create venv) # - Python imports look for top-level modules in sys.path roots # - For editable installs, src/ is on sys.path, so .so files there are importable # -# (usage: `just build-nvx cpy314` or with explicit NVX: `AUTOBAHN_USE_NVX=1 just build-nvx cpy314`) +# (usage: `just build-nvx cpy315` or with explicit NVX: `AUTOBAHN_USE_NVX=1 just build-nvx cpy315`) build-nvx venv="": (create venv) #!/usr/bin/env bash set -e @@ -652,7 +653,7 @@ install-dev-all: just install-dev ${venv} done -# Upgrade dependencies in a single environment (usage: `just upgrade cpy314`) +# Upgrade dependencies in a single environment (usage: `just upgrade cpy315`) upgrade venv="": (create venv) #!/usr/bin/env bash set -e @@ -680,7 +681,7 @@ upgrade-all: # -- Installation: Tools (Ruff, Sphinx, etc) # ----------------------------------------------------------------------------- -# Install minimal build tools for building wheels (usage: `just install-build-tools cpy314`) +# Install minimal build tools for building wheels (usage: `just install-build-tools cpy315`) # This is lighter than install-tools as it excludes dependencies like twine # (which depends on nh3, a Rust package that segfaults under QEMU ARM64 emulation) install-build-tools venv="": (create venv) @@ -707,7 +708,7 @@ install-build-tools venv="": (create venv) ${VENV_PYTHON} -m pip install -e .[build-tools] -# Install the development tools for this Package in a single environment (usage: `just install-tools cpy314`) +# Install the development tools for this Package in a single environment (usage: `just install-tools cpy315`) # This also builds NVX CFFI modules so that tests with AUTOBAHN_USE_NVX=1 work. install-tools venv="": (create venv) #!/usr/bin/env bash @@ -766,6 +767,7 @@ install-benchmark venv="": (create venv) (install venv) echo " - cpy312 (CPython 3.12)" echo " - cpy313 (CPython 3.13)" echo " - cpy314 (CPython 3.14)" + echo " - cpy315 (CPython 3.15)" echo " - pypy311 (PyPy 3.11)" exit 1 fi @@ -1052,7 +1054,7 @@ check-coverage venv="" use_nvx="": (install-tools venv) (install-dev venv) echo "--> Coverage report generated in docs/_build/html/coverage${NVX_SUFFIX}/index.html" -# Verify all WebSocket compression methods are available (usage: `just check-compressors cpy314 "permessage-deflate, permessage-brotli"`) +# Verify all WebSocket compression methods are available (usage: `just check-compressors cpy315 "permessage-deflate, permessage-brotli"`) check-compressors venv="" expect="permessage-brotli,permessage-bzip2,permessage-deflate,permessage-snappy": (install venv) #!/usr/bin/env bash set -e @@ -1124,7 +1126,7 @@ check-compressors venv="" expect="permessage-brotli,permessage-bzip2,permessage- echo "✅ Compression methods check completed" fi -# Verify all WAMP serializers are available (usage: `just check-serializers cpy314 "json, msgpack, cbor, ubjson, flatbuffers"`) +# Verify all WAMP serializers are available (usage: `just check-serializers cpy315 "json, msgpack, cbor, ubjson, flatbuffers"`) check-serializers venv="" expect="cbor,flatbuffers,json,msgpack,ubjson": (install venv) #!/usr/bin/env bash set -e @@ -1194,7 +1196,7 @@ check-serializers venv="" expect="cbor,flatbuffers,json,msgpack,ubjson": (instal # Check the vendored FlatBuffers version is in sync with zlmdb (data-in-transit # vs data-at-rest; both are used together by Crossbar.io). No-op if zlmdb is not -# installed. Usage: `just check-flatbuffers-sync cpy314` +# installed. Usage: `just check-flatbuffers-sync cpy315` check-flatbuffers-sync venv="": (install venv) #!/usr/bin/env bash set -e @@ -1220,14 +1222,14 @@ check-flatbuffers-sync venv="": (install venv) "${VENV_PYTHON}" "${TMP_SCRIPT}" rm -f "${TMP_SCRIPT}" -# Run all checks in single environment (usage: `just check cpy314`) +# Run all checks in single environment (usage: `just check cpy315`) check venv="": (check-compressors venv) (check-serializers venv) (check-format venv) (check-typing venv) (check-coverage-combined venv) # ----------------------------------------------------------------------------- # -- Unit tests # ----------------------------------------------------------------------------- -# Run the test suite for Twisted/trial and asyncio/pytest (usage: `just test cpy314`) +# Run the test suite for Twisted/trial and asyncio/pytest (usage: `just test cpy315`) test venv="" use_nvx="": (test-twisted venv use_nvx) (test-asyncio venv use_nvx) # Meta-recipe to run `test` on all environments @@ -1238,7 +1240,7 @@ test-all: just test ${venv} done -# Run basic autobahn library import test (usage: `just test-import cpy314`) +# Run basic autobahn library import test (usage: `just test-import cpy315`) test-import venv="": (install-tools venv) (install-dev venv) #!/usr/bin/env bash set -e @@ -1253,7 +1255,7 @@ test-import venv="": (install-tools venv) (install-dev venv) ${VENV_PYTHON} -c "from autobahn.wamp.message import Unregistered; print(f'\n{Unregistered.MESSAGE_TYPE}! ohh, yeah.\n')" -# Run the test suite for Twisted using trial (usage: `just test-twisted cpy314`) +# Run the test suite for Twisted using trial (usage: `just test-twisted cpy315`) test-twisted venv="" use_nvx="": (install-tools venv) (install-dev venv) #!/usr/bin/env bash set -e @@ -1294,7 +1296,7 @@ test-twisted venv="" use_nvx="": (install-tools venv) (install-dev venv) autobahn.wamp.test \ autobahn.nvx.test -# Run the test suite for asyncio using pytest (usage: `just test-asyncio cpy314`) +# Run the test suite for asyncio using pytest (usage: `just test-asyncio cpy315`) test-asyncio venv="" use_nvx="": (install-tools venv) (install-dev venv) #!/usr/bin/env bash set -e @@ -1813,7 +1815,7 @@ bump-flatbuffers: update-flatbuffers: ./scripts/update_flatbuffers.sh -# Build wheel only (usage: `just build cpy314`) +# Build wheel only (usage: `just build cpy315`) build venv="": (install-build-tools venv) #!/usr/bin/env bash set -e diff --git a/pyproject.toml b/pyproject.toml index 6c1df64ba..d7c5620de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet", From b4e66ae40bc03578cb8078c422a3ce8073457da3 Mon Sep 17 00:00:00 2001 From: Pravin Kamble Date: Sat, 10 Oct 2026 08:15:00 +0530 Subject: [PATCH 2/2] Add PyPy 3.12 support --- .audit/python315.md | 2 +- .github/workflows/README.md | 17 ++- .github/workflows/main.yml | 8 +- .github/workflows/release.yml | 136 ++++++++++++++++++ .github/workflows/wheels-arm64.yml | 18 +++ .github/workflows/wheels-docker.yml | 2 +- .github/workflows/wheels.yml | 12 +- .github/workflows/wstest.yml | 58 +++++++- README.md | 6 +- ...ockerfile.pypy312-bookworm-manylinux-arm64 | 27 ++++ .../Dockerfile.pypy312-trixie-manylinux-arm64 | 27 ++++ docs/changelog.rst | 2 +- docs/installation.rst | 4 +- docs/websocket/conformance.rst | 10 +- docs/wheels-inventory.rst | 18 ++- .../serdes/tests/test_cbor_cross_version.py | 2 +- hatch_build.py | 4 +- justfile | 4 +- src/autobahn/nvx/_utf8validator.py | 4 +- src/autobahn/nvx/_xormasker.py | 2 +- src/autobahn/twisted/component.py | 3 +- src/autobahn/util.py | 6 +- src/autobahn/wamp/cryptosign.py | 6 +- 23 files changed, 335 insertions(+), 43 deletions(-) create mode 100644 docker/Dockerfile.pypy312-bookworm-manylinux-arm64 create mode 100644 docker/Dockerfile.pypy312-trixie-manylinux-arm64 diff --git a/.audit/python315.md b/.audit/python315.md index ce6b83bf2..2e753bfe9 100644 --- a/.audit/python315.md +++ b/.audit/python315.md @@ -6,5 +6,5 @@ Submitted by: @p-r-a-v-i-n Date: 2026-09-27 -Related issue(s): +Related issue(s): #1963 Branch: p-r-a-v-i-n:python315 diff --git a/.github/workflows/README.md b/.github/workflows/README.md index f2976d706..b6e10d342 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -179,8 +179,8 @@ functionality to wheel installs including the flatc binary. |----------|--------------|----------|----------| | **main.yml** | `documentation` | `docs/_build/html/` | N/A | | **wstest.yml** | `wstest-results` | WebSocket conformance reports | N/A | -| **wheels.yml** | `wheels-macos-arm64` | macOS ARM64 wheels (cpy311-315, pypy311) | macOS arm64 | -| **wheels.yml** | `wheels-windows-x86_64` | Windows x64 wheels (cpy311-315, pypy311) | Windows x86_64 | +| **wheels.yml** | `wheels-macos-arm64` | macOS ARM64 wheels (cpy311-315, pypy311-312) | macOS arm64 | +| **wheels.yml** | `wheels-windows-x86_64` | Windows x64 wheels (cpy311-315, pypy311-312) | Windows x86_64 | | **wheels.yml** | `wheels-windows-arm64` | Windows ARM64 wheels (cpy311-315, no PyPy) | Windows arm64 | | **wheels.yml** | `linux-wheels-no-nvx` | Pure Python wheels (no NVX) | Linux x86_64 | | **wheels.yml** | `source-distribution` | `*.tar.gz` sdist | Linux (build host) | @@ -193,6 +193,8 @@ functionality to wheel installs including the flatc binary. | **wheels-arm64.yml** | `artifacts-arm64-cpython-3.15-musllinux_1_2_aarch64` | CPython 3.15 musllinux wheel | Linux aarch64 | | **wheels-arm64.yml** | `artifacts-arm64-pypy-3.11-bookworm-manylinux_2_36_aarch64` | PyPy 3.11 wheel (Debian 12) | Linux aarch64 | | **wheels-arm64.yml** | `artifacts-arm64-pypy-3.11-trixie-manylinux_2_38_aarch64` | PyPy 3.11 wheel (Debian 13) | Linux aarch64 | +| **wheels-arm64.yml** | `artifacts-arm64-pypy-3.12-bookworm-manylinux_2_36_aarch64` | PyPy 3.12 / pp80 wheel (Debian 12) | Linux aarch64 | +| **wheels-arm64.yml** | `artifacts-arm64-pypy-3.12-trixie-manylinux_2_38_aarch64` | PyPy 3.12 / pp80 wheel (Debian 13) | Linux aarch64 | **wheels-docker.yml artifact contents** (`artifacts-manylinux_2_28_x86_64`): - `cpy311-linux-x86_64-manylinux_2_28` @@ -201,6 +203,7 @@ functionality to wheel installs including the flatc binary. - `cpy314-linux-x86_64-manylinux_2_28` - `cpy315-linux-x86_64-manylinux_2_28` - `pypy311-linux-x86_64-manylinux_2_28` +- `pypy312-linux-x86_64-manylinux_2_28` ### 2. Artifact Consumer (release.yml) @@ -223,6 +226,8 @@ download action. It maps artifact names via the `check-workflows` job outputs: | `artifact_arm64_cp315_musl` | wheels-arm64.yml | `artifacts-arm64-cpython-3.15-musllinux_1_2_aarch64` | | `artifact_arm64_pypy_bookworm` | wheels-arm64.yml | `artifacts-arm64-pypy-3.11-bookworm-manylinux_2_36_aarch64` | | `artifact_arm64_pypy_trixie` | wheels-arm64.yml | `artifacts-arm64-pypy-3.11-trixie-manylinux_2_38_aarch64` | +| `artifact_arm64_pypy312_bookworm` | wheels-arm64.yml | `artifacts-arm64-pypy-3.12-bookworm-manylinux_2_36_aarch64` | +| `artifact_arm64_pypy312_trixie` | wheels-arm64.yml | `artifacts-arm64-pypy-3.12-trixie-manylinux_2_38_aarch64` | ## Platform Coverage @@ -230,11 +235,11 @@ download action. It maps artifact names via the `check-workflows` job outputs: | Platform | Architecture | Python Versions | Manylinux Tag | Workflow | |----------|--------------|-----------------|---------------|----------| -| Linux | x86_64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11 | manylinux_2_28 | wheels-docker.yml | +| Linux | x86_64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11/3.12 | manylinux_2_28 | wheels-docker.yml | | Linux | aarch64 | 3.11, 3.12, 3.13, 3.14, 3.15 | manylinux_2_28 | wheels-arm64.yml | -| Linux | aarch64 | PyPy 3.11 | manylinux_2_36/2_38 | wheels-arm64.yml | -| macOS | arm64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11 | N/A | wheels.yml | -| Windows | x86_64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11 | N/A | wheels.yml | +| Linux | aarch64 | PyPy 3.11/3.12 | manylinux_2_36/2_38 | wheels-arm64.yml | +| macOS | arm64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11/3.12 | N/A | wheels.yml | +| Windows | x86_64 | 3.11, 3.12, 3.13, 3.14, 3.15, PyPy 3.11/3.12 | N/A | wheels.yml | | Windows | arm64 | 3.11, 3.12, 3.13, 3.14, 3.15 (no PyPy) | N/A | wheels.yml | ### Why Manylinux Containers? diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 64e7baa89..996b9f2a8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -142,7 +142,7 @@ jobs: strategy: matrix: - python-env: [cpy315, cpy311, pypy311] + python-env: [cpy315, cpy311, pypy312, pypy311] steps: - name: Checkout code @@ -235,7 +235,7 @@ jobs: # (via install-dev -> autobahn[all]) so the cryptosign code paths run. strategy: matrix: - python-env: [cpy315, cpy314, cpy313, cpy312, cpy311, pypy311] + python-env: [cpy315, cpy314, cpy313, cpy312, cpy311, pypy312, pypy311] steps: - name: Checkout code @@ -521,7 +521,7 @@ jobs: strategy: matrix: - python-env: [cpy315, cpy311, pypy311] + python-env: [cpy315, cpy311, pypy312, pypy311] steps: - name: Checkout code @@ -684,7 +684,7 @@ jobs: strategy: matrix: - python-env: [cpy315, cpy311, pypy311] + python-env: [cpy315, cpy311, pypy312, pypy311] steps: - name: Checkout code diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c04eb2d63..7f6756ebf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,6 +40,8 @@ jobs: artifact_arm64_cp315_musl: ${{ steps.check.outputs.artifact_arm64_cp315_musl }} artifact_arm64_pypy_bookworm: ${{ steps.check.outputs.artifact_arm64_pypy_bookworm }} artifact_arm64_pypy_trixie: ${{ steps.check.outputs.artifact_arm64_pypy_trixie }} + artifact_arm64_pypy312_bookworm: ${{ steps.check.outputs.artifact_arm64_pypy312_bookworm }} + artifact_arm64_pypy312_trixie: ${{ steps.check.outputs.artifact_arm64_pypy312_trixie }} steps: - name: Check all required workflows completed @@ -165,6 +167,8 @@ jobs: core.setOutput('artifact_arm64_cp315_musl', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-cpython-3.15-musllinux_1_2_aarch64')); core.setOutput('artifact_arm64_pypy_bookworm', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-pypy-3.11-bookworm-manylinux_2_36_aarch64')); core.setOutput('artifact_arm64_pypy_trixie', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-pypy-3.11-trixie-manylinux_2_38_aarch64')); + core.setOutput('artifact_arm64_pypy312_bookworm', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-pypy-3.12-bookworm-manylinux_2_36_aarch64')); + core.setOutput('artifact_arm64_pypy312_trixie', await findArtifact(wheelsArm64RunId, 'artifacts-arm64-pypy-3.12-trixie-manylinux_2_38_aarch64')); console.log('─────────────────────────────────────────────────'); } @@ -586,6 +590,28 @@ jobs: continue-on-error: true # Download wstest conformance summary (explicit enumeration, no pattern) + - name: Download and verify ARM64 PyPy 3.12 Bookworm artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_pypy312_bookworm }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + + - name: Download and verify ARM64 PyPy 3.12 Trixie artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_pypy312_trixie }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download wstest conformance summary (quick) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -623,6 +649,15 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + - name: Download FlatBuffers generated code (pypy312) + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: flatbuffers-gen-pypy312 + path: ${{ github.workspace }}/flatbuffers-gen/pypy312/ + run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + # Download FlatBuffers schema files (explicit enumeration for each matrix env) - name: Download FlatBuffers schema files (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main @@ -651,6 +686,15 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + - name: Download FlatBuffers schema files (pypy312) + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: flatbuffers-schema-pypy312 + path: ${{ github.workspace }}/flatbuffers-schema/pypy312/ + run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + # Download SerDes conformance test results (explicit enumeration for each matrix env) - name: Download SerDes test results (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main @@ -679,6 +723,15 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + - name: Download SerDes test results (pypy312) + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: serdes-test-results-pypy312 + path: ${{ github.workspace }}/serdes-test-results/pypy312/ + run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + - name: Download WebSocket conformance HTML reports with-nvx (for RTD) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -982,6 +1035,10 @@ jobs: pypy311-linux-x86_64-manylinux_2_28 pypy311-linux-aarch64-manylinux_2_17 pypy311-win-amd64 + pypy312-macos-arm64 + pypy312-linux-x86_64-manylinux_2_28 + pypy312-linux-aarch64-manylinux_2_17 + pypy312-win-amd64 source - name: Validate all wheels before release (Final Gate) @@ -1548,6 +1605,28 @@ jobs: continue-on-error: true # Download wstest conformance summary (explicit enumeration, no pattern) + - name: Download and verify ARM64 PyPy 3.12 Bookworm artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_pypy312_bookworm }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + + - name: Download and verify ARM64 PyPy 3.12 Trixie artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_pypy312_trixie }} + path: ${{ github.workspace }}/wheelhouse-arm64/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Download wstest conformance summary (quick) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -1585,6 +1664,15 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + - name: Download FlatBuffers generated code (pypy312) + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: flatbuffers-gen-pypy312 + path: ${{ github.workspace }}/flatbuffers-gen/pypy312/ + run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + # Download FlatBuffers schema files (explicit enumeration for each matrix env) - name: Download FlatBuffers schema files (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main @@ -1613,6 +1701,15 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + - name: Download FlatBuffers schema files (pypy312) + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: flatbuffers-schema-pypy312 + path: ${{ github.workspace }}/flatbuffers-schema/pypy312/ + run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + # Download SerDes conformance test results (explicit enumeration for each matrix env) - name: Download SerDes test results (cpy315) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main @@ -1641,6 +1738,15 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true + - name: Download SerDes test results (pypy312) + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: serdes-test-results-pypy312 + path: ${{ github.workspace }}/serdes-test-results/pypy312/ + run-id: ${{ needs.check-all-workflows.outputs.main_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + - name: Download WebSocket conformance HTML reports with-nvx (for RTD) uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main with: @@ -1944,6 +2050,10 @@ jobs: pypy311-linux-x86_64-manylinux_2_28 pypy311-linux-aarch64-manylinux_2_17 pypy311-win-amd64 + pypy312-macos-arm64 + pypy312-linux-x86_64-manylinux_2_28 + pypy312-linux-aarch64-manylinux_2_17 + pypy312-win-amd64 source - name: Validate all wheels before release (Final Gate) @@ -2529,6 +2639,28 @@ jobs: retry-delay: 30 continue-on-error: true + - name: Download and verify ARM64 PyPy 3.12 Bookworm artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_pypy312_bookworm }} + path: ${{ github.workspace }}/dist/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + + - name: Download and verify ARM64 PyPy 3.12 Trixie artifacts with retry logic + uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main + with: + name: ${{ needs.check-all-workflows.outputs.artifact_arm64_pypy312_trixie }} + path: ${{ github.workspace }}/dist/ + run-id: ${{ needs.check-all-workflows.outputs.wheels_arm64_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + max-attempts: 5 + retry-delay: 30 + continue-on-error: true + - name: Force file system sync (post-download, pre-verification) run: | echo "======================================================================" @@ -2697,6 +2829,10 @@ jobs: pypy311-linux-x86_64-manylinux_2_28 pypy311-linux-aarch64-manylinux_2_17 pypy311-win-amd64 + pypy312-macos-arm64 + pypy312-linux-x86_64-manylinux_2_28 + pypy312-linux-aarch64-manylinux_2_17 + pypy312-win-amd64 source - name: Check if version already exists on PyPI diff --git a/.github/workflows/wheels-arm64.yml b/.github/workflows/wheels-arm64.yml index 83d9584ad..a56bdda03 100644 --- a/.github/workflows/wheels-arm64.yml +++ b/.github/workflows/wheels-arm64.yml @@ -171,6 +171,24 @@ jobs: build_type: "custom" python_versions: "pypy311" + # PyPy 3.12 / PyPy 8.0 on Debian 12 (Bookworm), pp80 ABI + - name: "pypy-3.12-bookworm-manylinux_2_36_aarch64" + dockerfile: "docker/Dockerfile.pypy312-bookworm-manylinux-arm64" + manylinux_tag: "manylinux_2_36_aarch64" + glibc_version: "2.36" + python_impl: "pypy" + build_type: "custom" + python_versions: "pypy312" + + # PyPy 3.12 / PyPy 8.0 on Debian 13 (Trixie), pp80 ABI + - name: "pypy-3.12-trixie-manylinux_2_38_aarch64" + dockerfile: "docker/Dockerfile.pypy312-trixie-manylinux-arm64" + manylinux_tag: "manylinux_2_38_aarch64" + glibc_version: "2.38" + python_impl: "pypy" + build_type: "custom" + python_versions: "pypy312" + steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/wheels-docker.yml b/.github/workflows/wheels-docker.yml index 848ff9586..27e6fe4ea 100644 --- a/.github/workflows/wheels-docker.yml +++ b/.github/workflows/wheels-docker.yml @@ -57,7 +57,7 @@ jobs: # musllinux_1_2 (Alpine / musl libc) wheels — CPython only. # The pypa musllinux image ships no PyPy under /opt/python, so we build - # cpy311-315 explicitly (not `build-all`, which includes pypy311). #1877 + # cpy311-315 explicitly (not `build-all`, which includes PyPy). #1877 - name: "musllinux_1_2_x86_64" base_image: "quay.io/pypa/musllinux_1_2_x86_64" build_envs: "cpy311 cpy312 cpy313 cpy314 cpy315" diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 2dc50558a..b614c6499 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -376,6 +376,13 @@ jobs: echo "" shell: bash + - name: Configure OpenSSL for cryptography source builds (macOS) + if: matrix.platform == 'macos' + run: | + brew install openssl@3 + echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" + shell: bash + - name: Build binary wheels with NVX (macOS) if: matrix.platform == 'macos' run: | @@ -495,11 +502,12 @@ jobs: # - x86_64: since 50.0.2, cryptography's Windows PyPy wheel is tagged # pypy311_pp80 (PyPy 8) only, while our pypy311 env is pinned to # PyPy 7.3 (pp73 ABI), so the pypy311 build falls back to the sdist. - # This step can be removed once both have wheels again (or we move to pp80). + # - PyPy 3.12: cryptography publishes no pypy312/pp80 wheel yet. + # This step can be removed once all three targets have wheels again. if: matrix.platform == 'windows' shell: pwsh run: | - $triplet = if ("${{ matrix.os }}" -eq "windows-11-arm") { "arm64-windows-static-md" } else { "x64-windows-static-md" } + $triplet = if ("${{ matrix.arch }}" -eq "arm64") { "arm64-windows-static-md" } else { "x64-windows-static-md" } vcpkg install --triplet $triplet --clean-after-build openssl "OPENSSL_DIR=$env:VCPKG_INSTALLATION_ROOT\installed\$triplet" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append diff --git a/.github/workflows/wstest.yml b/.github/workflows/wstest.yml index ca7facdd7..e39a8a71a 100644 --- a/.github/workflows/wstest.yml +++ b/.github/workflows/wstest.yml @@ -78,6 +78,7 @@ jobs: run: | just install cpy311 just install cpy315 + just install pypy312 just install pypy311 - name: Run all client test combinations (with-nvx and without-nvx) @@ -117,7 +118,7 @@ jobs: sleep 1 done - echo "==> Running client tests for all 6 combinations ($nvx_config)..." + echo "==> Running client tests for all 8 combinations ($nvx_config)..." # Client 1: Twisted + CPython 3.11 echo "==> Testing Twisted + cpy311 client ($nvx_config)..." @@ -143,7 +144,15 @@ jobs: echo "==> Testing asyncio + pypy311 client ($nvx_config)..." just wstest-testeeclient-asyncio pypy311 - echo "✅ All 6 client test combinations completed ($nvx_config)" + # Client 7: Twisted + PyPy 3.12 + echo "==> Testing Twisted + pypy312 client ($nvx_config)..." + just wstest-testeeclient-twisted pypy312 + + # Client 8: asyncio + PyPy 3.12 + echo "==> Testing asyncio + pypy312 client ($nvx_config)..." + just wstest-testeeclient-asyncio pypy312 + + echo "✅ All 8 client test combinations completed ($nvx_config)" # Give fuzzingserver time to write all reports to disk echo "==> Waiting for fuzzingserver to finish writing reports..." @@ -295,6 +304,7 @@ jobs: run: | just install cpy311 just install cpy315 + just install pypy312 just install pypy311 - name: Run server tests (with-nvx and without-nvx) @@ -314,7 +324,7 @@ jobs: echo "======================================================================" fi - echo "==> Starting 6 WebSocket server combinations for $nvx_config..." + echo "==> Starting 8 WebSocket server combinations for $nvx_config..." # Server 1: Twisted + CPython 3.11 on port 9011 echo "==> Starting Twisted + cpy311 server on port 9011 ($nvx_config)..." @@ -418,16 +428,50 @@ jobs: sleep 1 done - echo "✅ All 6 WebSocket servers running successfully for $nvx_config" + # Server 7: Twisted + PyPy 3.12 on port 9017 + echo "==> Starting Twisted + pypy312 server on port 9017 ($nvx_config)..." + nohup .venvs/pypy312/bin/python ./wstest/testee_server_tx.py --url ws://127.0.0.1:9017 > twisted-pypy312-$nvx_config.log 2>&1 & + echo $! > twisted-pypy312.pid + for i in {1..30}; do + if netstat -tuln | grep :9017 >/dev/null 2>&1; then + echo "✅ Twisted + pypy312 server running on port 9017" + break + fi + if [ $i -eq 30 ]; then + echo "❌ Twisted + pypy312 server failed to start on port 9017" + cat twisted-pypy312-$nvx_config.log + exit 1 + fi + sleep 1 + done + + # Server 8: asyncio + PyPy 3.12 on port 9018 + echo "==> Starting asyncio + pypy312 server on port 9018 ($nvx_config)..." + nohup .venvs/pypy312/bin/python ./wstest/testee_server_aio.py --url ws://127.0.0.1:9018 > asyncio-pypy312-$nvx_config.log 2>&1 & + echo $! > asyncio-pypy312.pid + for i in {1..30}; do + if netstat -tuln | grep :9018 >/dev/null 2>&1; then + echo "✅ asyncio + pypy312 server running on port 9018" + break + fi + if [ $i -eq 30 ]; then + echo "❌ asyncio + pypy312 server failed to start on port 9018" + cat asyncio-pypy312-$nvx_config.log + exit 1 + fi + sleep 1 + done + + echo "✅ All 8 WebSocket servers running successfully for $nvx_config" - echo "==> Running fuzzingclient tests against all 6 servers ($nvx_config)..." + echo "==> Running fuzzingclient tests against all 8 servers ($nvx_config)..." just wstest-fuzzingclient "" "" ${{ env.TEST_MODE }} echo "✅ Server tests completed ($nvx_config)" # Stop all servers for this configuration echo "==> Stopping all WebSocket servers for $nvx_config..." - for pid_file in twisted-cpy311.pid asyncio-cpy311.pid twisted-cpy315.pid asyncio-cpy315.pid twisted-pypy311.pid asyncio-pypy311.pid; do + for pid_file in twisted-cpy311.pid asyncio-cpy311.pid twisted-cpy315.pid asyncio-cpy315.pid twisted-pypy311.pid asyncio-pypy311.pid twisted-pypy312.pid asyncio-pypy312.pid; do if [ -f "$pid_file" ]; then kill $(cat "$pid_file") 2>/dev/null || true rm -f "$pid_file" @@ -686,7 +730,7 @@ jobs: run: | echo "==> WebSocket Conformance Testing Complete ✅" echo "Test Mode: ${{ env.TEST_MODE }}" - echo "Matrix tested: (client|server) × (twisted|asyncio) × (cpy311|cpy315|pypy311) × (with-nvx|without-nvx)" + echo "Matrix tested: (client|server) × (twisted|asyncio) × (cpy311|cpy315|pypy311|pypy312) × (with-nvx|without-nvx)" echo "" echo "Reports generated for both NVX configurations:" for nvx_config in "with-nvx" "without-nvx"; do diff --git a/README.md b/README.md index b4d5f4410..fa9ac3955 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Autobahn|Python publishes source distributions and pre-built wheels on [PyPI](https://pypi.org/project/autobahn/) and [GitHub Releases](https://github.com/crossbario/autobahn-python/releases). The current release line requires Python 3.11 or later and publishes -wheels for CPython 3.11 through 3.15 and PyPy 3.11 across supported +wheels for CPython 3.11 through 3.15 and PyPy 3.11 and 3.12 across supported Linux, macOS, and Windows targets. The recommended installation method is: @@ -383,7 +383,7 @@ All serializer dependencies provide binary wheels for: - **macOS**: x86_64 (Intel), ARM64 (Apple Silicon) - **Windows**: x86_64 (AMD64), ARM64 - **Python**: 3.11, 3.12, 3.13, 3.14, 3.15 (including 3.14t free-threaded) -- **Implementations**: CPython, PyPy 3.11+ +- **Implementations**: CPython, PyPy 3.11 and 3.12 #### Backwards Compatibility @@ -504,7 +504,7 @@ Available via `pip install autobahn[nvx]`: - **Operating Systems**: Linux (glibc/musl), macOS, Windows - **Architectures**: x86_64 (Intel/AMD), ARM64 (Apple Silicon, AWS Graviton) - **Python Versions**: 3.11, 3.12, 3.13, 3.14, 3.15 (including free-threaded 3.14t) -- **Implementations**: CPython, PyPy 3.11+ +- **Implementations**: CPython, PyPy 3.11 and 3.12 **All optional dependencies install cleanly without:** - System libraries (except optional python-snappy) diff --git a/docker/Dockerfile.pypy312-bookworm-manylinux-arm64 b/docker/Dockerfile.pypy312-bookworm-manylinux-arm64 new file mode 100644 index 000000000..22f63f15d --- /dev/null +++ b/docker/Dockerfile.pypy312-bookworm-manylinux-arm64 @@ -0,0 +1,27 @@ +# PyPy 3.12 / PyPy 8.0 on Debian 12 (Bookworm) - manylinux_2_36_aarch64 +FROM pypy:3.12-bookworm + +LABEL maintainer="The Crossbar.io Project " +LABEL manylinux.tag="manylinux_2_36_aarch64" +LABEL glibc.version="2.36" + +ENV PYPY_DISABLE_JIT=1 +ENV PYPY_GC_NTHREADS=1 +ENV PYPY_FORCE_CPU_COUNT=1 +ENV PATH="/root/.cargo/bin:${PATH}" + +# PyPy 3.12/pp80 has no cryptography wheel yet, so Rust and OpenSSL are an +# intentional part of this builder rather than an accidental pip fallback. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + libffi-dev \ + libssl-dev \ + libsnappy-dev \ + && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal \ + && pip install --upgrade pip \ + && pip install auditwheel patchelf \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /io diff --git a/docker/Dockerfile.pypy312-trixie-manylinux-arm64 b/docker/Dockerfile.pypy312-trixie-manylinux-arm64 new file mode 100644 index 000000000..5514df973 --- /dev/null +++ b/docker/Dockerfile.pypy312-trixie-manylinux-arm64 @@ -0,0 +1,27 @@ +# PyPy 3.12 / PyPy 8.0 on Debian 13 (Trixie) - manylinux_2_38_aarch64 +FROM pypy:3.12-trixie + +LABEL maintainer="The Crossbar.io Project " +LABEL manylinux.tag="manylinux_2_38_aarch64" +LABEL glibc.version="2.38" + +ENV PYPY_DISABLE_JIT=1 +ENV PYPY_GC_NTHREADS=1 +ENV PYPY_FORCE_CPU_COUNT=1 +ENV PATH="/root/.cargo/bin:${PATH}" + +# PyPy 3.12/pp80 has no cryptography wheel yet, so Rust and OpenSSL are an +# intentional part of this builder rather than an accidental pip fallback. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + libffi-dev \ + libssl-dev \ + libsnappy-dev \ + && curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal \ + && pip install --upgrade pip \ + && pip install auditwheel patchelf \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /io diff --git a/docs/changelog.rst b/docs/changelog.rst index b28e6eccd..6dc2e305d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -40,7 +40,7 @@ Changelog **Build & CI/CD** -* Add CPython 3.15 support across package metadata, CI, developer environments, binary wheel builds, release validation, and documentation. +* Add CPython 3.15 and PyPy 3.12 / PyPy 8.0 (``pp80``) support across package metadata, CI, developer environments, binary wheel builds, release validation, and documentation (#1963). * Add CalVer / PEP 440 version-management ``just`` recipes (``file-version``, ``bump-dev``, ``bump-next``, ``prep-release``) mirroring Crossbar.io, and document the versioning policy in ``CONTRIBUTING.md`` (#1894) * Add ``ruff check --select ANN,UP,TCH`` (annotation presence, ``pyupgrade`` modern syntax, ``TYPE_CHECKING`` imports) to the ``just check-typing`` recipe so annotation/style regressions are caught in the ``quality-checks`` CI job. The existing gaps in ``src/autobahn/`` are ratcheted via an explicit ``--ignore`` allowlist to be removed module-by-module (#1839); all other ``UP``/``TC`` rules are enforced immediately, and generated code is excluded. The annotation rules are scoped to this recipe via the command line rather than the global ``[tool.ruff.lint]`` select, so the repo-wide ``check-format`` gate is unaffected (#1840) * Fix the aarch64 CPython 3.14 wheel shipping the free-threaded ABI (``cp314t``) in the GIL ``cp314`` slot (26.6.x). Root cause: manylinux images pre-install both the GIL and free-threaded 3.14 under ``/opt/python`` and prepend them to ``PATH``, and ``uv`` resolved ``cpython-3.14`` to the free-threaded interpreter (first on ``PATH``). The ``create`` recipe now drops free-threaded ``…t/bin`` dirs from ``PATH`` for GIL envs so ``uv`` selects the GIL build. As defence-in-depth, ``just build`` also asserts (via ``_check-venv-abi``) that the interpreter's GIL/free-threaded status matches the env and aborts on mismatch, so a wrong-ABI wheel can never be published. A reserved ``cpy314t`` env spec (``cpython-3.14t``) is added for a future free-threaded wheel variant (#1875) diff --git a/docs/installation.rst b/docs/installation.rst index 8d960c038..022a94856 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -38,7 +38,7 @@ Here are the configurations supported by |ab|: - yes - yes - Pre-built wheels are published for supported platforms. - * - PyPy 3.11 + * - PyPy 3.11 and 3.12 - yes - yes - Pre-built wheels are published for supported platforms. @@ -90,7 +90,7 @@ using `Pip `_: This is the recommended installation method. Pip will select a pre-built wheel when one matches your Python implementation, Python version, operating system, and CPU architecture. Current releases publish -wheels for CPython 3.11 through 3.15 and PyPy 3.11 across the supported +wheels for CPython 3.11 through 3.15 and PyPy 3.11 and 3.12 across the supported Linux, macOS, and Windows targets documented in :doc:`wheels-inventory`. You can also specify *install variants* (see below). E.g. to install Twisted automatically as a dependency diff --git a/docs/websocket/conformance.rst b/docs/websocket/conformance.rst index 2287fbc7a..2a6d8a3b3 100644 --- a/docs/websocket/conformance.rst +++ b/docs/websocket/conformance.rst @@ -62,28 +62,32 @@ Client Testing just wstest-testeeclient-twisted cpy311 just wstest-testeeclient-twisted cpy315 just wstest-testeeclient-twisted pypy311 + just wstest-testeeclient-twisted pypy312 just wstest-testeeclient-asyncio cpy311 just wstest-testeeclient-asyncio cpy315 just wstest-testeeclient-asyncio pypy311 + just wstest-testeeclient-asyncio pypy312 Test results will be generated in the ``.wstest/`` directory: * ``.wstest/clients/`` - Client test results -Server Testing (6 combinations) +Server Testing (8 combinations) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: bash - # Terminal 1-6: Start all server combinations + # Terminal 1-8: Start all server combinations just wstest-testeeserver-twisted cpy311 "ws://127.0.0.1:9011" just wstest-testeeserver-asyncio cpy311 "ws://127.0.0.1:9012" just wstest-testeeserver-twisted cpy315 "ws://127.0.0.1:9013" just wstest-testeeserver-asyncio cpy315 "ws://127.0.0.1:9014" just wstest-testeeserver-twisted pypy311 "ws://127.0.0.1:9015" just wstest-testeeserver-asyncio pypy311 "ws://127.0.0.1:9016" + just wstest-testeeserver-twisted pypy312 "ws://127.0.0.1:9017" + just wstest-testeeserver-asyncio pypy312 "ws://127.0.0.1:9018" - # Terminal 7: Run testsuite client against all 6 servers + # Terminal 9: Run testsuite client against all 8 servers just wstest-fuzzingclient # Or for full mode: just wstest-fuzzingclient "" "" full diff --git a/docs/wheels-inventory.rst b/docs/wheels-inventory.rst index 01e2617af..dd13000ce 100644 --- a/docs/wheels-inventory.rst +++ b/docs/wheels-inventory.rst @@ -64,6 +64,9 @@ Linux x86_64 * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-linux_x86_64.whl`` - ❌ No (pure Python) + * - PyPy 3.12 (PyPy 8.0) + - ``autobahn-{version}-pp312-pypy312_pp80-linux_x86_64.whl`` + - ❌ No (pure Python) **Note:** Linux x86_64 wheels are built WITHOUT NVX to ensure maximum compatibility across different Linux distributions. Users on modern x86_64 systems can still benefit from NVX by installing from source or using platform-specific builds. @@ -130,6 +133,9 @@ macOS ARM64 (Apple Silicon) * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-macosx_15_0_arm64.whl`` - ✅ Yes (binary) + * - PyPy 3.12 (PyPy 8.0) + - ``autobahn-{version}-pp312-pypy312_pp80-macosx_15_0_arm64.whl`` + - ✅ Yes (binary) Windows x86_64 ^^^^^^^^^^^^^^ @@ -161,6 +167,9 @@ Windows x86_64 * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-win_amd64.whl`` - ✅ Yes (binary) + * - PyPy 3.12 (PyPy 8.0) + - ``autobahn-{version}-pp312-pypy312_pp80-win_amd64.whl`` + - ✅ Yes (binary) Windows ARM64 ^^^^^^^^^^^^^ @@ -272,6 +281,9 @@ Linux ARM64 (PyPy) - Debian 12 * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-manylinux_2_36_aarch64.whl`` - ✅ Yes (binary) + * - PyPy 3.12 (PyPy 8.0) + - ``autobahn-{version}-pp312-pypy312_pp80-manylinux_2_36_aarch64.whl`` + - ✅ Yes (binary) **Compatibility:** Requires glibc 2.36 or later (Debian 12+, Ubuntu 22.04+) @@ -290,6 +302,9 @@ Linux ARM64 (PyPy) - Debian 13 * - PyPy 3.11 - ``autobahn-{version}-pp311-pypy311_pp73-manylinux_2_38_aarch64.whl`` - ✅ Yes (binary) + * - PyPy 3.12 (PyPy 8.0) + - ``autobahn-{version}-pp312-pypy312_pp80-manylinux_2_38_aarch64.whl`` + - ✅ Yes (binary) **Compatibility:** Requires glibc 2.38 or later (Debian 13+, Ubuntu 24.04+) @@ -439,7 +454,8 @@ Common Tags **Python tags:** * ``cp311``, ``cp312``, ``cp313``, ``cp314``, ``cp315`` - CPython 3.11, 3.12, 3.13, 3.14, 3.15 -* ``pp311`` - PyPy 3.11 +* ``pp311`` - PyPy 3.11 using the pp73 ABI +* ``pp312`` - PyPy 3.12 / PyPy 8.0 using the pp80 ABI **Platform tags:** diff --git a/examples/serdes/tests/test_cbor_cross_version.py b/examples/serdes/tests/test_cbor_cross_version.py index 30ea1d906..765a4660c 100644 --- a/examples/serdes/tests/test_cbor_cross_version.py +++ b/examples/serdes/tests/test_cbor_cross_version.py @@ -11,7 +11,7 @@ For the WAMP wire protocol this is only safe if both implementations encode and decode CBOR *byte-for-byte identically*. These tests turn that requirement into an assertion that runs on BOTH runtimes in CI (the ``test-serdes`` matrix covers -``cpy311``/``cpy315`` and ``pypy311``): +``cpy311``/``cpy315`` and ``pypy311``/``pypy312``): 1. ``test_cbor2_version_matches_runtime`` -- a provenance guard: fail loudly if the installed cbor2 is not the version the dependency markers intend for this diff --git a/hatch_build.py b/hatch_build.py index 3a2776a18..70f39243f 100644 --- a/hatch_build.py +++ b/hatch_build.py @@ -94,7 +94,9 @@ def initialize(self, version, build_data): def _get_ext_suffix(self): """Get the extension suffix for the current Python interpreter. - E.g., '.cpython-311-x86_64-linux-gnu.so' or '.pypy311-pp73-x86_64-linux-gnu.so' + E.g., '.cpython-311-x86_64-linux-gnu.so', + '.pypy311-pp73-x86_64-linux-gnu.so', or + '.pypy312-pp80-x86_64-linux-gnu.so'. """ return sysconfig.get_config_var("EXT_SUFFIX") or ".so" diff --git a/justfile b/justfile index 55c72a47c..798c5ba2a 100644 --- a/justfile +++ b/justfile @@ -80,7 +80,7 @@ VENV_DIR := PROJECT_DIR / '.venvs' # Define a justfile-local variable for our environments. # PyPy publishes no Windows ARM64 interpreter, so win_arm64 is CPython-only. -ENVS := if os() + "-" + arch() == "windows-aarch64" { 'cpy315 cpy314 cpy313 cpy312 cpy311' } else { 'cpy315 cpy314 cpy313 cpy312 cpy311 pypy311' } +ENVS := if os() + "-" + arch() == "windows-aarch64" { 'cpy315 cpy314 cpy313 cpy312 cpy311' } else { 'cpy315 cpy314 cpy313 cpy312 cpy311 pypy312 pypy311' } # Package version files, kept in sync (CalVer YY.M.PATCH[.devN], PEP 440). PY_VERSION_FILE := 'src/autobahn/_version.py' @@ -102,6 +102,7 @@ _get-spec short_name: cpy313) echo "cpython-3.13{{PY_PLATFORM_SUFFIX}}";; # cpython-3.13.5-linux-x86_64-gnu cpy312) echo "cpython-3.12{{PY_PLATFORM_SUFFIX}}";; # cpython-3.12.11-linux-x86_64-gnu cpy311) echo "cpython-3.11{{PY_PLATFORM_SUFFIX}}";; # cpython-3.11.13-linux-x86_64-gnu + pypy312) echo "pypy-3.12.14";; # PyPy 8.0 / pp80 ABI pypy311) echo "pypy-3.11.15";; # PyPy 7.3.23 = last pp73 ABI; pinned on purpose (PyPy 8.0 = new pp80 ABI) *) echo "Unknown environment: {{short_name}}" >&2; exit 1;; esac @@ -768,6 +769,7 @@ install-benchmark venv="": (create venv) (install venv) echo " - cpy313 (CPython 3.13)" echo " - cpy314 (CPython 3.14)" echo " - cpy315 (CPython 3.15)" + echo " - pypy312 (PyPy 3.12 / PyPy 8.0)" echo " - pypy311 (PyPy 3.11)" exit 1 fi diff --git a/src/autobahn/nvx/_utf8validator.py b/src/autobahn/nvx/_utf8validator.py index 170e520fa..c55bd11ea 100644 --- a/src/autobahn/nvx/_utf8validator.py +++ b/src/autobahn/nvx/_utf8validator.py @@ -78,7 +78,9 @@ # and build context detection (wheel distribution vs. local source install) extra_compile_args = get_compile_args() -with open(os.path.join(os.path.dirname(__file__), "_utf8validator.c")) as fd: +with open( + os.path.join(os.path.dirname(__file__), "_utf8validator.c"), encoding="utf8" +) as fd: c_source = fd.read() ffi.set_source( "_nvx_utf8validator", diff --git a/src/autobahn/nvx/_xormasker.py b/src/autobahn/nvx/_xormasker.py index dfc30ee1c..bdf88b103 100644 --- a/src/autobahn/nvx/_xormasker.py +++ b/src/autobahn/nvx/_xormasker.py @@ -76,7 +76,7 @@ # and build context detection (wheel distribution vs. local source install) extra_compile_args = get_compile_args() -with open(os.path.join(os.path.dirname(__file__), "_xormasker.c")) as fd: +with open(os.path.join(os.path.dirname(__file__), "_xormasker.c"), encoding="utf8") as fd: c_source = fd.read() ffi.set_source( "_nvx_xormasker", diff --git a/src/autobahn/twisted/component.py b/src/autobahn/twisted/component.py index 7383ff55b..232d4eb32 100644 --- a/src/autobahn/twisted/component.py +++ b/src/autobahn/twisted/component.py @@ -165,7 +165,8 @@ def _create_transport_endpoint(reactor, endpoint_config): trust_root = None cert_fname = tls.get("trust_root", None) if cert_fname is not None: - trust_root = Certificate.loadPEM(open(cert_fname).read()) + with open(cert_fname, encoding="utf8") as cert_file: + trust_root = Certificate.loadPEM(cert_file.read()) context = optionsForClientTLS(hostname, trustRoot=trust_root) elif isinstance(tls, CertificateOptions): diff --git a/src/autobahn/util.py b/src/autobahn/util.py index ff175ef9e..93901ab94 100644 --- a/src/autobahn/util.py +++ b/src/autobahn/util.py @@ -941,7 +941,7 @@ def machine_id() -> str: if platform.isLinux(): try: # why this? see: http://0pointer.de/blog/projects/ids.html - with open("/var/lib/dbus/machine-id") as f: + with open("/var/lib/dbus/machine-id", encoding="utf8") as f: return f.read().strip() except: # Non-dbus using Linux, get a hostname @@ -1026,7 +1026,7 @@ def write_keyfile(filepath, tags, msg): """ Internal helper, write the given tags to the given file- """ - with open(filepath, "w") as f: + with open(filepath, "w", encoding="utf8") as f: f.write(msg) for tag, value in tags.items(): if value: @@ -1060,7 +1060,7 @@ def parse_keyfile(key_path: str, private: bool = True) -> OrderedDict: allowed_tags.extend(["private-key-ed25519", "private-key-eth"]) tags = OrderedDict() # type: ignore - with open(key_path) as key_file: + with open(key_path, encoding="utf8") as key_file: got_blankline = False for line in key_file.readlines(): if line.strip() == "": diff --git a/src/autobahn/wamp/cryptosign.py b/src/autobahn/wamp/cryptosign.py index 0cb3a5717..3db480524 100644 --- a/src/autobahn/wamp/cryptosign.py +++ b/src/autobahn/wamp/cryptosign.py @@ -281,7 +281,7 @@ def _read_signify_ed25519_signature(signature_file): http://man.openbsd.org/OpenBSD-current/man1/signify.1 """ - with open(signature_file) as f: + with open(signature_file, encoding="utf8") as f: # signature file format: 2nd line is base64 of 'Ed' || 8 random octets || 64 octets Ed25519 signature sig = binascii.a2b_base64(f.read().splitlines()[1])[10:] if len(sig) != 64: @@ -297,7 +297,7 @@ def _read_signify_ed25519_pubkey(pubkey_file): http://man.openbsd.org/OpenBSD-current/man1/signify.1 """ - with open(pubkey_file) as f: + with open(pubkey_file, encoding="utf8") as f: # signature file format: 2nd line is base64 of 'Ed' || 8 random octets || 32 octets Ed25519 public key pubkey = binascii.a2b_base64(f.read().splitlines()[1])[10:] if len(pubkey) != 32: @@ -328,7 +328,7 @@ def _qrcode_from_signify_ed25519_pubkey(pubkey_file, mode="text"): import qrcode - with open(pubkey_file) as f: + with open(pubkey_file, encoding="utf8") as f: pubkey = f.read().splitlines()[1] qr = qrcode.QRCode(box_size=3, error_correction=qrcode.ERROR_CORRECT_L)