Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/build-runstats.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# Based on the `builds` job of
# https://github.com/grantjenks/python-runstats/blob/v2.0.0/.github/workflows/release.yml
name: Build runstats wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'runstats version to build (git tag without leading v, e.g. 2.0.0)'
required: true
default: '2.0.0'
pull_request:
paths:
- '.github/workflows/build-runstats.yml'
- 'patches/runstats/**'

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '2.0.0' }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read # to fetch code (actions/checkout)

env:
RUNSTATS_VERSION: ${{ inputs.version || '2.0.0' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

jobs:
setup:
uses: $/.github/workflows/_setup.yml

build_wheels:
needs: [setup]
name: Build runstats ${{ inputs.version || '2.0.0' }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
strategy:
fail-fast: false
matrix:
python: ["cp312", "cp313", "cp314", "cp314t"]

steps:
- name: Checkout runstats v${{ env.RUNSTATS_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: grantjenks/python-runstats
ref: v${{ env.RUNSTATS_VERSION }}
persist-credentials: false

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

- name: Patch runstats source
run: git apply python-wheels/patches/runstats/${{ env.RUNSTATS_VERSION }}/*.patch

- name: Build wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
output-dir: wheelhouse/
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# setup.py has no [build-system] table, so pypa/build's default isolated
# venv never sees CIBW_BEFORE_BUILD's cython install and silently falls
# back to a pure-Python wheel (CLAUDE.md gotcha 57).
CIBW_BEFORE_BUILD: pip install cython setuptools
CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
CIBW_TEST_REQUIRES: pytest
# test/__init__.py exists, so build-from-checkout's own runstats/ would
# shadow the installed wheel under pytest's default import mode; stage
# only tests/ into an empty cwd instead (gotcha 25). Also sidesteps
# tox.ini's --cov-fail-under=100, which measures unrelated dev checks.
CIBW_TEST_SOURCES: tests
# setup.py falls back to a pure-Python wheel if cythonize/build_ext raises
# (bare try/except around the Extension build) -- assert the compiled
# extension actually loaded before trusting the test suite (gotcha 91).
CIBW_TEST_COMMAND: >-
python -c "import runstats._core; assert runstats._core.__file__.endswith('.so'), runstats._core.__file__"
&& python -m pytest tests
&& python tests/benchmark.py

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: runstats-${{ env.RUNSTATS_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error

publish:
name: Publish runstats ${{ inputs.version || '2.0.0' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: runstats-${{ inputs.version || '2.0.0' }}-*-manylinux_riscv64
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
From 05bc58febe93abc362e2204aaa7d4504d2bc24a8 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Mon, 7 Sep 2026 14:56:43 +0200
Subject: [PATCH] core: define Statistics.__rmul__ explicitly instead of
aliasing __mul__

Cython 3.x compiles cdef class special methods from the textual "def" it
sees; the "__rmul__ = __mul__" alias only adds a dict entry after the
class body runs, it does not retroactively wire the type's nb_multiply
slot to also try the reflected method, so "2 * stats" raises TypeError
on the compiled _core.Statistics while the pure-Python core.Statistics
(an ordinary class, where CPython's own slot machinery checks __dict__
dynamically) still supports it. Affects any build against a modern
Cython, independent of architecture.

Upstream-Status: To upstream [not yet submitted; RISE's python-wheels port policy disallows opening issues/PRs against third-party repos]
---
runstats/core.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/runstats/core.py b/runstats/core.py
index bc9f14e..3af5275 100644
--- a/runstats/core.py
+++ b/runstats/core.py
@@ -241,7 +241,9 @@ class Statistics:
# https://stackoverflow.com/q/33218006/232571
return that._mul(self) # pragma: no cover

- __rmul__ = __mul__
+ def __rmul__(self, that):
+ """Multiply by a scalar to change Statistics weighting."""
+ return self.__mul__(that)

def _imul(self, that):
"""Multiply by a scalar to change Statistics weighting in-place."""
--
2.50.1 (Apple Git-155)