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
13 changes: 11 additions & 2 deletions .github/workflows/build-pandas.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,14 @@ jobs:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# CIBW_ENVIRONMENT replaces pandas's [tool.cibuildwheel] environment
# table wholesale, so its own LDFLAGS has to be restated here.
# pandas 3.1's scalar fallback (any CPU other than x86_64/aarch64)
# calls xsimd::load_unaligned/reduce_add/broadcast on xsimd::common,
# but xsimd 14.2 defines XSIMD_NO_SUPPORTED_ARCHITECTURE on a plain
# riscv64 target (no RVV) and then omits xsimd_api.hpp; the emulated
# arch makes xsimd keep the API.
CIBW_ENVIRONMENT: >-
LDFLAGS=-Wl,--strip-all
CXXFLAGS=-DXSIMD_WITH_EMULATED=1
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
PIP_ONLY_BINARY=numpy,hypothesis
# cibuildwheel copies the source into a fresh container per build, and
Expand All @@ -98,11 +104,14 @@ jobs:
# git describe reads when the meson build resolves the version (gotcha
# 315). CIBW_BEFORE_BUILD replaces pandas's [tool.cibuildwheel]
# before-build wholesale, so re-run it here, then commit and re-tag in
# the same container before the build reads the version.
# the same container before the build reads the version. pandas 3.1
# dropped the script, so skip it when absent.
CIBW_BEFORE_BUILD: >-
if [ -f {package}/scripts/cibw_before_build.sh ]; then
PACKAGE_DIR={package} bash {package}/scripts/cibw_before_build.sh &&
git -C {package} -c user.name=ci -c user.email=ci@localhost commit -am "cibw before-build" &&
git -C {package} tag -f v${{ env.PANDAS_VERSION }}
git -C {package} tag -f v${{ env.PANDAS_VERSION }};
fi

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down
1 change: 1 addition & 0 deletions docs/packages/pandas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,4 @@ versions:
- filename: pandas-3.0.6-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl
sha256: 4bc771048b8e7ac4191168e180177e373be9adc5911fc3a4277e22eb25bced61
requires-python: '>=3.11'
- version: 3.1.0
48 changes: 48 additions & 0 deletions patches/pandas/3.1.0/0001-disable-nan-test-on-riscv64.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
From 5e254d628784dd803ff9b492b06db8118b4bbaee Mon Sep 17 00:00:00 2001
From: Julien Stephan <jstephan@baylibre.com>
Date: Thu, 26 Mar 2026 13:41:01 +0100
Subject: [PATCH] disable nan test on riscv64

Upstream-Status: To upstream [To send when we will add riscv64 support upstream]
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
---
pandas/tests/tools/test_to_datetime.py | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py
index c61137d..2db1b74 100644
--- a/pandas/tests/tools/test_to_datetime.py
+++ b/pandas/tests/tools/test_to_datetime.py
@@ -11,6 +11,7 @@ from datetime import (
)
from decimal import Decimal
import locale
+import platform
import re
import zoneinfo

@@ -1864,6 +1865,10 @@ class TestToDatetimeUnit:
tm.assert_index_equal(result, expected)

# with a nan!
+ # skip test on riscv64
+ if platform.machine() == 'riscv64':
+ return
+
result = pd.to_datetime(np.array([item, np.nan]), unit=unit, cache=cache)
assert result.isna()[1]
tm.assert_index_equal(result[:1], expected)
@@ -4743,6 +4748,10 @@ def test_to_datetime_lxml_elementunicoderesult_with_format(cache):


def test_to_datetime_missing_component_no_runtime_warning():
+ # skip test on riscv64
+ if platform.machine() == 'riscv64':
+ return
+
df = pd.DataFrame(
{
"year": [2023, 2023],
--
2.54.0

51 changes: 51 additions & 0 deletions patches/pandas/3.1.0/0002-make-nan-to-int64-casts-explicit.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Wed, 28 Oct 2026 19:35:17 +0000
Subject: [PATCH] core: make NaN to int64 casts explicit for
to_datetime/to_timedelta

Casting NaN to int64 is undefined behaviour in C and the result is
architecture dependent: INT64_MIN (iNaT) on x86, 0 on ARM and INT64_MAX
on riscv64. to_datetime(unit=...) and to_timedelta/to_datetime assembly
with float columns relied on the x86 result and, on riscv64, tried to
convert INT64_MAX seconds/hours before the NaN positions were masked,
raising OutOfBounds errors. Set the masked positions to iNaT explicitly.

Upstream-Status: To upstream [To send when we will add riscv64 support upstream]
Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
pandas/core/arrays/timedeltas.py | 3 +++
pandas/core/tools/datetimes.py | 3 +++
2 files changed, 6 insertions(+)

diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py
index b24e372..56cb4d3 100644
--- a/pandas/core/arrays/timedeltas.py
+++ b/pandas/core/arrays/timedeltas.py
@@ -1312,6 +1312,9 @@ def sequence_to_td64ns(
# (which results in 'us' unit instead of 'ns')
with np.errstate(invalid="ignore"):
int_data = data.astype(np.int64)
+ # NaN -> int64 is undefined behaviour in C: x86 yields INT64_MIN (iNaT),
+ # ARM yields 0 and riscv64 yields INT64_MAX. Make it explicit.
+ int_data[mask] = iNaT
# On ARM, float-to-int64 overflow saturates to INT64_MAX
# instead of wrapping, which makes the data == int_data
# check pass incorrectly for OOB values like float(2**63).
diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py
index 1dc48d2..4412ff1 100644
--- a/pandas/core/tools/datetimes.py
+++ b/pandas/core/tools/datetimes.py
@@ -601,6 +601,9 @@ def _to_datetime_with_unit(
mask = np.isnan(arg)
with np.errstate(invalid="ignore"):
int_values = arg.astype(np.int64)
+ # NaN -> int64 is undefined behaviour in C: x86 yields INT64_MIN (iNaT),
+ # ARM yields 0 and riscv64 yields INT64_MAX. Make it explicit.
+ int_values[mask] = iNaT
# On ARM, float-to-int64 overflow saturates to INT64_MAX
# instead of wrapping, which makes the arg == int_values
# check pass incorrectly for OOB values like float(2**63).
--
2.43.0

Loading