rsnumpy 1.0.3__tar.gz → 1.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/.github/workflows/ci.yml +7 -5
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/.github/workflows/release.yml +0 -30
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/.gitignore +3 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/Cargo.lock +11 -44
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/Cargo.toml +12 -7
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/PKG-INFO +13 -2
- rsnumpy-1.0.6/build_wheel.sh +155 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/pyproject.toml +15 -2
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/__init__.py +67 -9
- rsnumpy-1.0.6/rust-toolchain.toml +2 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/src/indexing.rs +23 -6
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/src/lib.rs +139 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/test/run_test.py +0 -7
- rsnumpy-1.0.3/build_wheel.sh +0 -93
- rsnumpy-1.0.3/plots.py +0 -1086
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/LICENSE +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/README.md +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/RELEASE_NOTES.md +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/array_methods.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/array_ops.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/char.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/io.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/linalg/__init__.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/math_functions.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/matlib.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/polynomial/__init__.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/random/__init__.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/python/rsnumpy/statistics.py +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/src/fft.rs +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/src/linalg.rs +0 -0
- {rsnumpy-1.0.3 → rsnumpy-1.0.6}/src/random.rs +0 -0
|
@@ -2,9 +2,9 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [main, master, dev,
|
|
5
|
+
branches: [main, master, dev, main-dev]
|
|
6
6
|
pull_request:
|
|
7
|
-
branches: [main, master, dev,
|
|
7
|
+
branches: [main, master, dev, main-dev]
|
|
8
8
|
|
|
9
9
|
# 同一分支/PR 的新推送会取消仍在进行的旧运行,节省 CI 时间。
|
|
10
10
|
concurrency:
|
|
@@ -26,6 +26,11 @@ jobs:
|
|
|
26
26
|
matrix:
|
|
27
27
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
28
28
|
python-version: ["3.10", "3.13", "3.14"]
|
|
29
|
+
include:
|
|
30
|
+
- os: ubuntu-latest
|
|
31
|
+
python-version: "3.10"
|
|
32
|
+
- os: windows-latest
|
|
33
|
+
python-version: "3.10"
|
|
29
34
|
steps:
|
|
30
35
|
- uses: actions/checkout@v4
|
|
31
36
|
|
|
@@ -57,9 +62,6 @@ jobs:
|
|
|
57
62
|
- name: Install built wheel
|
|
58
63
|
run: python -m pip install --no-index --find-links dist rsnumpy
|
|
59
64
|
|
|
60
|
-
- name: Install test dependencies
|
|
61
|
-
run: python -m pip install numpy
|
|
62
|
-
|
|
63
65
|
- name: Run smoke test
|
|
64
66
|
run: python test/run_test.py
|
|
65
67
|
|
|
@@ -25,16 +25,6 @@ jobs:
|
|
|
25
25
|
steps:
|
|
26
26
|
- uses: actions/checkout@v4
|
|
27
27
|
|
|
28
|
-
- name: Cache Rust build artifacts
|
|
29
|
-
uses: actions/cache@v4
|
|
30
|
-
with:
|
|
31
|
-
path: |
|
|
32
|
-
~/.cargo/registry
|
|
33
|
-
~/.cargo/git
|
|
34
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
35
|
-
restore-keys: |
|
|
36
|
-
${{ runner.os }}-cargo-
|
|
37
|
-
|
|
38
28
|
- name: Build wheels (${{ matrix.target }})
|
|
39
29
|
uses: PyO3/maturin-action@v1
|
|
40
30
|
with:
|
|
@@ -57,16 +47,6 @@ jobs:
|
|
|
57
47
|
steps:
|
|
58
48
|
- uses: actions/checkout@v4
|
|
59
49
|
|
|
60
|
-
- name: Cache Rust build artifacts
|
|
61
|
-
uses: actions/cache@v4
|
|
62
|
-
with:
|
|
63
|
-
path: |
|
|
64
|
-
~/.cargo/registry
|
|
65
|
-
~/.cargo/git
|
|
66
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
67
|
-
restore-keys: |
|
|
68
|
-
${{ runner.os }}-cargo-
|
|
69
|
-
|
|
70
50
|
- uses: actions/setup-python@v5
|
|
71
51
|
with:
|
|
72
52
|
python-version: ${{ matrix.python-version }}
|
|
@@ -95,16 +75,6 @@ jobs:
|
|
|
95
75
|
steps:
|
|
96
76
|
- uses: actions/checkout@v4
|
|
97
77
|
|
|
98
|
-
- name: Cache Rust build artifacts
|
|
99
|
-
uses: actions/cache@v4
|
|
100
|
-
with:
|
|
101
|
-
path: |
|
|
102
|
-
~/.cargo/registry
|
|
103
|
-
~/.cargo/git
|
|
104
|
-
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
105
|
-
restore-keys: |
|
|
106
|
-
${{ runner.os }}-cargo-
|
|
107
|
-
|
|
108
78
|
- uses: actions/setup-python@v5
|
|
109
79
|
with:
|
|
110
80
|
python-version: ${{ matrix.python-version }}
|
|
@@ -61,9 +61,9 @@ dependencies = [
|
|
|
61
61
|
|
|
62
62
|
[[package]]
|
|
63
63
|
name = "cc"
|
|
64
|
-
version = "1.2.
|
|
64
|
+
version = "1.2.66"
|
|
65
65
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
66
|
-
checksum = "
|
|
66
|
+
checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
|
|
67
67
|
dependencies = [
|
|
68
68
|
"find-msvc-tools",
|
|
69
69
|
"jobserver",
|
|
@@ -260,18 +260,6 @@ dependencies = [
|
|
|
260
260
|
"slab",
|
|
261
261
|
]
|
|
262
262
|
|
|
263
|
-
[[package]]
|
|
264
|
-
name = "getrandom"
|
|
265
|
-
version = "0.3.4"
|
|
266
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
267
|
-
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|
268
|
-
dependencies = [
|
|
269
|
-
"cfg-if",
|
|
270
|
-
"libc",
|
|
271
|
-
"r-efi 5.3.0",
|
|
272
|
-
"wasip2",
|
|
273
|
-
]
|
|
274
|
-
|
|
275
263
|
[[package]]
|
|
276
264
|
name = "getrandom"
|
|
277
265
|
version = "0.4.3"
|
|
@@ -281,7 +269,7 @@ dependencies = [
|
|
|
281
269
|
"cfg-if",
|
|
282
270
|
"js-sys",
|
|
283
271
|
"libc",
|
|
284
|
-
"r-efi
|
|
272
|
+
"r-efi",
|
|
285
273
|
"rand_core",
|
|
286
274
|
"wasm-bindgen",
|
|
287
275
|
]
|
|
@@ -337,11 +325,11 @@ dependencies = [
|
|
|
337
325
|
|
|
338
326
|
[[package]]
|
|
339
327
|
name = "jobserver"
|
|
340
|
-
version = "0.1.
|
|
328
|
+
version = "0.1.35"
|
|
341
329
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
342
|
-
checksum = "
|
|
330
|
+
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
|
|
343
331
|
dependencies = [
|
|
344
|
-
"getrandom
|
|
332
|
+
"getrandom",
|
|
345
333
|
"libc",
|
|
346
334
|
]
|
|
347
335
|
|
|
@@ -382,9 +370,9 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
|
|
382
370
|
|
|
383
371
|
[[package]]
|
|
384
372
|
name = "lzma-rust2"
|
|
385
|
-
version = "0.16.
|
|
373
|
+
version = "0.16.5"
|
|
386
374
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
387
|
-
checksum = "
|
|
375
|
+
checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b"
|
|
388
376
|
dependencies = [
|
|
389
377
|
"sha2",
|
|
390
378
|
]
|
|
@@ -607,12 +595,6 @@ dependencies = [
|
|
|
607
595
|
"proc-macro2",
|
|
608
596
|
]
|
|
609
597
|
|
|
610
|
-
[[package]]
|
|
611
|
-
name = "r-efi"
|
|
612
|
-
version = "5.3.0"
|
|
613
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
614
|
-
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
615
|
-
|
|
616
598
|
[[package]]
|
|
617
599
|
name = "r-efi"
|
|
618
600
|
version = "6.0.0"
|
|
@@ -626,7 +608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
626
608
|
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
|
627
609
|
dependencies = [
|
|
628
610
|
"chacha20",
|
|
629
|
-
"getrandom
|
|
611
|
+
"getrandom",
|
|
630
612
|
"rand_core",
|
|
631
613
|
]
|
|
632
614
|
|
|
@@ -674,7 +656,7 @@ dependencies = [
|
|
|
674
656
|
|
|
675
657
|
[[package]]
|
|
676
658
|
name = "rsnumpy"
|
|
677
|
-
version = "1.0.
|
|
659
|
+
version = "1.0.6"
|
|
678
660
|
dependencies = [
|
|
679
661
|
"ndarray",
|
|
680
662
|
"num-complex",
|
|
@@ -868,15 +850,6 @@ version = "1.0.24"
|
|
|
868
850
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
869
851
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
870
852
|
|
|
871
|
-
[[package]]
|
|
872
|
-
name = "wasip2"
|
|
873
|
-
version = "1.0.4+wasi-0.2.12"
|
|
874
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
875
|
-
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
|
876
|
-
dependencies = [
|
|
877
|
-
"wit-bindgen",
|
|
878
|
-
]
|
|
879
|
-
|
|
880
853
|
[[package]]
|
|
881
854
|
name = "wasm-bindgen"
|
|
882
855
|
version = "0.2.126"
|
|
@@ -922,12 +895,6 @@ dependencies = [
|
|
|
922
895
|
"unicode-ident",
|
|
923
896
|
]
|
|
924
897
|
|
|
925
|
-
[[package]]
|
|
926
|
-
name = "wit-bindgen"
|
|
927
|
-
version = "0.57.1"
|
|
928
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
929
|
-
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
|
930
|
-
|
|
931
898
|
[[package]]
|
|
932
899
|
name = "zeroize"
|
|
933
900
|
version = "1.9.0"
|
|
@@ -946,7 +913,7 @@ dependencies = [
|
|
|
946
913
|
"crc32fast",
|
|
947
914
|
"deflate64",
|
|
948
915
|
"flate2",
|
|
949
|
-
"getrandom
|
|
916
|
+
"getrandom",
|
|
950
917
|
"hmac",
|
|
951
918
|
"indexmap",
|
|
952
919
|
"lzma-rust2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rsnumpy"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.6"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ name = "rsnumpy"
|
|
|
9
9
|
crate-type = ["cdylib"]
|
|
10
10
|
|
|
11
11
|
[dependencies]
|
|
12
|
-
pyo3 = { version = "0.29.0", features = ["extension-module"
|
|
12
|
+
pyo3 = { version = "0.29.0", features = ["extension-module"] }
|
|
13
13
|
ndarray = { version = "0.17.2", features = ["rayon", "serde"] }
|
|
14
14
|
rayon = "1.12.0"
|
|
15
15
|
rand = "0.10.2"
|
|
@@ -29,9 +29,14 @@ blas = ["ndarray/blas"]
|
|
|
29
29
|
opt-level = 3
|
|
30
30
|
lto = "fat"
|
|
31
31
|
codegen-units = 1
|
|
32
|
-
|
|
33
|
-
strip = true
|
|
32
|
+
strip = "symbols"
|
|
34
33
|
|
|
35
|
-
[profile.
|
|
36
|
-
opt-level =
|
|
37
|
-
debug =
|
|
34
|
+
[profile.dev]
|
|
35
|
+
opt-level = 0
|
|
36
|
+
debug = true
|
|
37
|
+
|
|
38
|
+
[lints.clippy]
|
|
39
|
+
too_many_arguments = "allow"
|
|
40
|
+
type_complexity = "allow"
|
|
41
|
+
cast_precision_loss = "allow"
|
|
42
|
+
cast_sign_loss = "allow"
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rsnumpy
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
3
|
+
Version: 1.0.6
|
|
5
4
|
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
6
12
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
7
13
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
8
19
|
License-File: LICENSE
|
|
9
20
|
Summary: A Rust-powered NumPy-compatible array library.
|
|
10
21
|
Author: YJ-Niu
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Build Rust lib into Python wheel using maturin
|
|
5
|
+
# Usage: ./build_wheel.sh [--release|--debug] [--out-dir DIR] [--python PYTHON_EXEC]
|
|
6
|
+
#
|
|
7
|
+
# 跨平台:macOS / Linux 直接运行;Windows 请在 Git Bash 或 WSL 中运行
|
|
8
|
+
# (venv 布局会自动切换为 Scripts/python.exe)。
|
|
9
|
+
|
|
10
|
+
# ========== 从 Cargo.toml 读取元信息 ==========
|
|
11
|
+
NAME=$(grep -m1 '^name = ' Cargo.toml | sed 's/name = "\(.*\)"/\1/')
|
|
12
|
+
VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
|
|
13
|
+
|
|
14
|
+
echo "Project: $NAME, Version: $VERSION"
|
|
15
|
+
|
|
16
|
+
# ========== 同步版本到 pyproject.toml ==========
|
|
17
|
+
sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml
|
|
18
|
+
rm -f pyproject.toml.bak
|
|
19
|
+
echo " -> pyproject.toml version updated to $VERSION"
|
|
20
|
+
|
|
21
|
+
# ========== 同步版本到 python/rsplotlib/__init__.py ==========
|
|
22
|
+
sed -i.bak "s/^__version__ = \".*\"/__version__ = \"$VERSION\"/" python/rsplotlib/__init__.py 2>/dev/null || true
|
|
23
|
+
rm -f python/rsplotlib/__init__.py.bak
|
|
24
|
+
echo " -> python/rsplotlib/__init__.py __version__ updated to $VERSION"
|
|
25
|
+
|
|
26
|
+
# ========== 检查 cargo 是否可用(maturin 构建需要) ==========
|
|
27
|
+
if ! command -v cargo >/dev/null 2>&1; then
|
|
28
|
+
echo "Error: 'cargo' not found in PATH." >&2
|
|
29
|
+
echo "Please install Rust and Cargo from https://rustup.rs/" >&2
|
|
30
|
+
echo "After installation, run: source \$HOME/.cargo/env" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# ========== 依据 rust-toolchain.toml 准备工具链与 clippy 组件 ==========
|
|
35
|
+
# 项目用 rust-toolchain.toml 固定了 Rust channel;rustup 会自动据此选择工具链,
|
|
36
|
+
# 但构建前的 clippy 检查依赖 clippy 组件,全新环境未必已安装,这里显式补齐。
|
|
37
|
+
if [[ -f rust-toolchain.toml ]]; then
|
|
38
|
+
RUST_CHANNEL=$(grep -m1 '^channel *= *' rust-toolchain.toml | sed -E 's/.*"([^"]+)".*/\1/')
|
|
39
|
+
if [[ -n "${RUST_CHANNEL:-}" ]]; then
|
|
40
|
+
echo "Pinned Rust toolchain (rust-toolchain.toml): $RUST_CHANNEL"
|
|
41
|
+
if command -v rustup >/dev/null 2>&1; then
|
|
42
|
+
# 幂等:已安装则为空操作;离线且缺失时失败被忽略,交由后续 clippy 步骤明确报错。
|
|
43
|
+
rustup toolchain install "$RUST_CHANNEL" >/dev/null 2>&1 || true
|
|
44
|
+
rustup component add clippy --toolchain "$RUST_CHANNEL" >/dev/null 2>&1 || true
|
|
45
|
+
else
|
|
46
|
+
echo " -> rustup not found; using the cargo/clippy already on PATH." >&2
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
RELEASE=true
|
|
52
|
+
OUT_DIR="wheelhouse"
|
|
53
|
+
|
|
54
|
+
# 如果 wheelhouse 目录存在, 则删除
|
|
55
|
+
if [[ -d "$OUT_DIR" ]]; then
|
|
56
|
+
rm -rf "$OUT_DIR"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Default python executable; may be overridden by --python or positional arg
|
|
60
|
+
PYTHON_EXEC="python"
|
|
61
|
+
# Flag set when the user explicitly provided a Python executable
|
|
62
|
+
PYTHON_EXEC_SET=""
|
|
63
|
+
|
|
64
|
+
# ========== 平台探测:Windows(Git Bash/MSYS/Cygwin) 的 venv 是 Scripts/ 且解释器带 .exe ==========
|
|
65
|
+
case "$(uname -s 2>/dev/null || echo unknown)" in
|
|
66
|
+
MINGW*|MSYS*|CYGWIN*) VENV_BIN=".venv/Scripts"; VENV_PY=".venv/Scripts/python.exe" ;;
|
|
67
|
+
*) VENV_BIN=".venv/bin"; VENV_PY=".venv/bin/python" ;;
|
|
68
|
+
esac
|
|
69
|
+
|
|
70
|
+
# 若存在本地 venv 则激活(让其中的 maturin 进入 PATH);不存在也不报错。
|
|
71
|
+
if [[ -f "$VENV_BIN/activate" ]]; then
|
|
72
|
+
# shellcheck source=/dev/null
|
|
73
|
+
source "$VENV_BIN/activate"
|
|
74
|
+
fi
|
|
75
|
+
while [[ $# -gt 0 ]]; do
|
|
76
|
+
case $1 in
|
|
77
|
+
--release) RELEASE=true; shift ;;
|
|
78
|
+
--debug) RELEASE=false; shift ;;
|
|
79
|
+
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
|
80
|
+
--python) PYTHON_EXEC="$2"; PYTHON_EXEC_SET=1; shift 2 ;;
|
|
81
|
+
*) PYTHON_EXEC="$1"; PYTHON_EXEC_SET=1; shift ;;
|
|
82
|
+
esac
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
# If a local .venv exists and user didn't explicitly set Python, prefer it
|
|
86
|
+
if [[ -f "$VENV_PY" && -z "$PYTHON_EXEC_SET" ]]; then
|
|
87
|
+
PYTHON_EXEC="$VENV_PY"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Ensure chosen Python executable exists or is runnable
|
|
91
|
+
if ! command -v "$PYTHON_EXEC" >/dev/null 2>&1 && [[ ! -x "$PYTHON_EXEC" && ! -f "$PYTHON_EXEC" ]]; then
|
|
92
|
+
echo "Error: Python executable '$PYTHON_EXEC' not found or not executable." >&2
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Prefer a maturin on PATH, otherwise try running maturin via the chosen Python
|
|
97
|
+
if command -v maturin >/dev/null 2>&1; then
|
|
98
|
+
MATURIN_MODE="path"
|
|
99
|
+
elif "$PYTHON_EXEC" -c "import maturin" >/dev/null 2>&1; then
|
|
100
|
+
MATURIN_MODE="python-module"
|
|
101
|
+
else
|
|
102
|
+
echo "Error: maturin not found. Install it in your chosen Python (e.g. '$PYTHON_EXEC -m pip install maturin')." >&2
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# ========== 构建前的 Fmt 静态检查(-check:任何错误都当作错误) ==========
|
|
107
|
+
echo "Running fmt checks (cargo fmt --all -- --check) ..."
|
|
108
|
+
if ! cargo fmt --all -- --check; then
|
|
109
|
+
echo "Error: fmt checks failed. Fix the warnings above before building." >&2
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
echo " -> fmt checks passed."
|
|
113
|
+
|
|
114
|
+
# ========== 构建前的 Clippy 静态检查(-D warnings:任何告警都当作错误) ==========
|
|
115
|
+
echo "Running clippy checks (cargo clippy --all-targets -- -D warnings) ..."
|
|
116
|
+
if ! cargo clippy --all-targets -- -D warnings; then
|
|
117
|
+
echo "Error: clippy checks failed. Fix the warnings above before building." >&2
|
|
118
|
+
exit 1
|
|
119
|
+
fi
|
|
120
|
+
echo " -> clippy checks passed."
|
|
121
|
+
|
|
122
|
+
BUILD_ARGS=()
|
|
123
|
+
if $RELEASE; then BUILD_ARGS+=(--release); else BUILD_ARGS+=(--debug); fi
|
|
124
|
+
|
|
125
|
+
mkdir -p "$OUT_DIR"
|
|
126
|
+
echo "Building wheel into $OUT_DIR using $PYTHON_EXEC (release=$RELEASE)"
|
|
127
|
+
if [[ "$MATURIN_MODE" == "path" ]]; then
|
|
128
|
+
maturin build "${BUILD_ARGS[@]}" -o "$OUT_DIR" -i "$PYTHON_EXEC"
|
|
129
|
+
else
|
|
130
|
+
# run maturin as a module under the chosen Python
|
|
131
|
+
"$PYTHON_EXEC" -m maturin build "${BUILD_ARGS[@]}" -o "$OUT_DIR" -i "$PYTHON_EXEC"
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# Locate the built wheel and install it into the local venv
|
|
135
|
+
WHEEL=$(ls -t "$OUT_DIR"/*.whl 2>/dev/null | head -n1 || true)
|
|
136
|
+
if [[ -n "$WHEEL" && -f "$WHEEL" ]]; then
|
|
137
|
+
echo "Wheel built: $WHEEL"
|
|
138
|
+
if [[ -f "$VENV_PY" ]]; then
|
|
139
|
+
echo "Installing into .venv ..."
|
|
140
|
+
# --no-deps:本地 wheel 已含全部绑定代码,无需重新解析依赖。
|
|
141
|
+
# 优先用 uv(显式 --python 指向本 venv,避免依赖是否已 activate);无 uv 时回退到 pip。
|
|
142
|
+
if command -v uv >/dev/null 2>&1; then
|
|
143
|
+
uv pip install --python "$VENV_PY" --reinstall --no-deps "$WHEEL"
|
|
144
|
+
else
|
|
145
|
+
"$VENV_PY" -m pip install --force-reinstall --no-deps "$WHEEL"
|
|
146
|
+
fi
|
|
147
|
+
echo "Installed into .venv successfully."
|
|
148
|
+
else
|
|
149
|
+
echo "Warning: .venv not found; skip install. Wheels available in $OUT_DIR" >&2
|
|
150
|
+
fi
|
|
151
|
+
else
|
|
152
|
+
echo "Warning: no .whl file found in $OUT_DIR; skip install." >&2
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
echo "Done."
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rsnumpy"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.6"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "A Rust-powered NumPy-compatible array library."
|
|
10
10
|
readme = "README.md"
|
|
@@ -13,12 +13,25 @@ authors = [
|
|
|
13
13
|
{ name = "YJ-Niu" },
|
|
14
14
|
]
|
|
15
15
|
classifiers = [
|
|
16
|
-
"License :: OSI Approved :: MIT License",
|
|
17
16
|
"Programming Language :: Rust",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
18
24
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
19
25
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: MacOS",
|
|
28
|
+
"Operating System :: Microsoft :: Windows",
|
|
29
|
+
"Operating System :: POSIX :: Linux",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
20
31
|
]
|
|
21
32
|
|
|
33
|
+
dependencies = []
|
|
34
|
+
|
|
22
35
|
[tool.maturin]
|
|
23
36
|
module-name = "rsnumpy._core"
|
|
24
37
|
python-source = "python"
|
|
@@ -12,7 +12,6 @@ Examples:
|
|
|
12
12
|
2.0
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
import builtins
|
|
16
15
|
import rsnumpy._core as _core
|
|
17
16
|
from rsnumpy._core import ndarray_iter as NdArrayIter
|
|
18
17
|
# ========== 子模块导入和函数挂载 ==========
|
|
@@ -29,7 +28,7 @@ from .random import random_module as _random_module
|
|
|
29
28
|
from . import char as _char_module
|
|
30
29
|
from . import matlib as _matlib_module
|
|
31
30
|
|
|
32
|
-
__version__ = "1.0.
|
|
31
|
+
__version__ = "1.0.5"
|
|
33
32
|
|
|
34
33
|
class ArrayFlags:
|
|
35
34
|
"""数组内存布局信息,与 NumPy 的 np.ndarray.flags 兼容。"""
|
|
@@ -119,6 +118,9 @@ class ndarray:
|
|
|
119
118
|
if _raw_data is not None:
|
|
120
119
|
# 原始数据存储:用一个虚拟 Rust 数组占位
|
|
121
120
|
self._array = _core.zeros((len(data),) if isinstance(data, (list, tuple)) else (1,))
|
|
121
|
+
elif not isinstance(data, (list, tuple)) and hasattr(data, '__array_interface__'):
|
|
122
|
+
# 实现数组接口协议的对象(如 PIL 图像)→ 由 Rust 底层解码缓冲区
|
|
123
|
+
self._array, _dtype = _from_array_interface(data)
|
|
122
124
|
else:
|
|
123
125
|
if isinstance(data, tuple):
|
|
124
126
|
data = list(data)
|
|
@@ -126,6 +128,11 @@ class ndarray:
|
|
|
126
128
|
# 不规则列表(子列表长度不同),存储为原始数据
|
|
127
129
|
self._raw_data = data
|
|
128
130
|
self._array = _core.zeros((len(data),))
|
|
131
|
+
elif _has_string(data):
|
|
132
|
+
# 字符串数据 → 存储为原始 Python 数据
|
|
133
|
+
self._raw_data = list(data) if isinstance(data, (list, tuple)) else [data]
|
|
134
|
+
self._array = _core.zeros((len(self._raw_data),))
|
|
135
|
+
_dtype = "string_"
|
|
129
136
|
elif _has_complex(data):
|
|
130
137
|
# 包含复数 → 存储为 _complex_data
|
|
131
138
|
flat = _flatten_data(data)
|
|
@@ -274,7 +281,7 @@ class ndarray:
|
|
|
274
281
|
# 展开省略号(...),补充完整切片以匹配数组维度
|
|
275
282
|
if isinstance(key, tuple):
|
|
276
283
|
new_key = []
|
|
277
|
-
ellipsis_count =
|
|
284
|
+
ellipsis_count = _core.isum([1 for k in key if k is Ellipsis])
|
|
278
285
|
if ellipsis_count > 0:
|
|
279
286
|
non_ellipsis = [k for k in key if k is not Ellipsis]
|
|
280
287
|
fill = self.ndim - len(non_ellipsis)
|
|
@@ -311,7 +318,7 @@ class ndarray:
|
|
|
311
318
|
key = (_ndarray_to_index_list(key)
|
|
312
319
|
if hasattr(key, '_array') else key,)
|
|
313
320
|
if isinstance(key, tuple):
|
|
314
|
-
_core.setitem_multi(self._array, key, list(self.shape), value)
|
|
321
|
+
_core.setitem_multi(self._array, key, list(self.shape), _setitem_value(value))
|
|
315
322
|
else:
|
|
316
323
|
self._array[key] = value
|
|
317
324
|
|
|
@@ -916,7 +923,7 @@ class _float64:
|
|
|
916
923
|
def _format_float_repr_1d(values):
|
|
917
924
|
"""格式化 1D float64 数组的 repr,带逗号和对齐(匹配 NumPy)。"""
|
|
918
925
|
fmt = [format_float_scalar(v) for v in values]
|
|
919
|
-
max_w =
|
|
926
|
+
max_w = _core.imax([len(f) for f in fmt]) if fmt else 0
|
|
920
927
|
parts = [f.rjust(max_w) for f in fmt]
|
|
921
928
|
return "[" + ", ".join(parts) + "]"
|
|
922
929
|
|
|
@@ -1198,6 +1205,57 @@ def _flatten_data(data):
|
|
|
1198
1205
|
return flat
|
|
1199
1206
|
|
|
1200
1207
|
|
|
1208
|
+
def _has_string(data):
|
|
1209
|
+
"""检查(展平后的)数据中是否包含字符串。"""
|
|
1210
|
+
flat, _ = _flatten_check(data)
|
|
1211
|
+
return any(isinstance(v, str) for v in flat)
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
def _setitem_value(value):
|
|
1215
|
+
"""规范化赋值右值供 Rust setitem_multi 使用:
|
|
1216
|
+
标量原样返回;ndarray 或嵌套列表展平为 C 序浮点列表(逐元素赋值)。"""
|
|
1217
|
+
if _is_ndarray(value):
|
|
1218
|
+
return [float(v) for v in _flatten_data(value._array.tolist())]
|
|
1219
|
+
if value.__class__.__name__ == 'ndarray' and hasattr(value, 'tolist'):
|
|
1220
|
+
return [float(v) for v in _flatten_data(value.tolist())]
|
|
1221
|
+
if isinstance(value, (list, tuple)):
|
|
1222
|
+
return [float(v) for v in _flatten_data(value)]
|
|
1223
|
+
return value
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
# 数组接口协议 typestr 的 (kind, itemsize) → rsnumpy dtype 名称
|
|
1227
|
+
_ARRAY_INTERFACE_DTYPE = {
|
|
1228
|
+
('b', 1): 'bool',
|
|
1229
|
+
('u', 1): 'uint8', ('i', 1): 'int8',
|
|
1230
|
+
('u', 2): 'uint16', ('i', 2): 'int16',
|
|
1231
|
+
('u', 4): 'uint32', ('i', 4): 'int32',
|
|
1232
|
+
('u', 8): 'uint64', ('i', 8): 'int64',
|
|
1233
|
+
('f', 4): 'float32', ('f', 8): 'float64',
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
def _from_array_interface(obj):
|
|
1238
|
+
"""将实现数组接口协议的对象(如 PIL 图像)解码为 (Rust 数组, dtype)。
|
|
1239
|
+
|
|
1240
|
+
缓冲区的字节解码由 Rust 底层 `_core.from_buffer_typed` 完成。
|
|
1241
|
+
"""
|
|
1242
|
+
ai = obj.__array_interface__
|
|
1243
|
+
shape = tuple(ai['shape'])
|
|
1244
|
+
typestr = ai['typestr']
|
|
1245
|
+
data = ai['data']
|
|
1246
|
+
if isinstance(data, tuple):
|
|
1247
|
+
# (指针, 只读标志) 形式,无法直接读取 → 退回到 tobytes()
|
|
1248
|
+
data = obj.tobytes()
|
|
1249
|
+
elif not isinstance(data, (bytes, bytearray)):
|
|
1250
|
+
data = bytes(data)
|
|
1251
|
+
kind, itemsize = typestr[1], int(typestr[2:])
|
|
1252
|
+
dtype = _ARRAY_INTERFACE_DTYPE.get((kind, itemsize))
|
|
1253
|
+
if dtype is None:
|
|
1254
|
+
raise TypeError(f"Unsupported array interface type: {typestr}")
|
|
1255
|
+
arr = _core.from_buffer_typed(bytes(data), typestr, list(shape))
|
|
1256
|
+
return arr, dtype
|
|
1257
|
+
|
|
1258
|
+
|
|
1201
1259
|
def _is_rectangular(data):
|
|
1202
1260
|
"""检查嵌套列表是否为矩形(所有子列表长度相同)。"""
|
|
1203
1261
|
if not isinstance(data, (list, tuple)):
|
|
@@ -1843,12 +1901,12 @@ def nditer(a, order='C', op_flags=None, flags=None):
|
|
|
1843
1901
|
arrays = [ndarray(x) for x in a]
|
|
1844
1902
|
# 计算广播形状:每个维度取最大值
|
|
1845
1903
|
all_shapes = [x.shape for x in arrays]
|
|
1846
|
-
max_ndim =
|
|
1904
|
+
max_ndim = _core.imax([len(s) for s in all_shapes])
|
|
1847
1905
|
padded = []
|
|
1848
1906
|
for s in all_shapes:
|
|
1849
1907
|
pad = [1] * (max_ndim - len(s)) + list(s)
|
|
1850
1908
|
padded.append(pad)
|
|
1851
|
-
bcast_shape = tuple(
|
|
1909
|
+
bcast_shape = tuple(_core.imax([p[i] for p in padded]) for i in range(max_ndim))
|
|
1852
1910
|
# 广播每个数组到统一形状
|
|
1853
1911
|
bcast_arrays = []
|
|
1854
1912
|
for arr in arrays:
|
|
@@ -1909,7 +1967,7 @@ def nditer(a, order='C', op_flags=None, flags=None):
|
|
|
1909
1967
|
def _iter_f_order(dim, coords):
|
|
1910
1968
|
if dim < 0:
|
|
1911
1969
|
# coords 是反向顺序 [last_dim, ..., first_dim],需反转
|
|
1912
|
-
idx =
|
|
1970
|
+
idx = _core.isum([c * s for c, s in zip(reversed(coords), c_strides)])
|
|
1913
1971
|
if 0 <= idx < size:
|
|
1914
1972
|
val = flat_arr[idx].item()
|
|
1915
1973
|
yield _to_val(val, idx)
|
|
@@ -1960,7 +2018,7 @@ def iscomplex(x):
|
|
|
1960
2018
|
|
|
1961
2019
|
def _broadcast_shape(*shapes):
|
|
1962
2020
|
"""计算多个数组的广播形状。"""
|
|
1963
|
-
max_ndim =
|
|
2021
|
+
max_ndim = _core.imax([len(s) for s in shapes])
|
|
1964
2022
|
result = []
|
|
1965
2023
|
for i in range(max_ndim):
|
|
1966
2024
|
dims = set()
|