python: drop the NumPy runtime dependency (zero third-party deps) (#317)

Makes the Python binding truly dependency-free (Task 5b). `pip install wickra` now pulls **zero** third-party packages, and `import wickra` never imports NumPy (verified: `"numpy" not in sys.modules` after a batch call).

## What changed
- **Inputs** accept any sequence or buffer of numbers — `array.array`, `memoryview`, a NumPy `ndarray`, or a plain `list` — via `Vec` extraction (newtypes `Buf1`/`BufI64`). `PyBuffer` is unavailable under `abi3-py39`, and `unsafe_code = forbid` rules out a zero-copy slice, so inputs are copied once (negligible vs. compute).
- **Single-output `batch(...)`** returns a stdlib `array.array('d')` (native buffer protocol → `numpy.asarray` zero-copy).
- **Multi-output `batch(...)`** returns a buffer-protocol `Matrix` preserving `.shape`, integer-row and `[i, j]` access, and `.tolist()`.
- **NumPy** moves to an optional extra (`pip install wickra[numpy]`); it is never required.
- Streaming `update(...)` is unchanged; results are numerically identical.

## Implementation
- Trait `IntoPyData` + a `matrix()`/`f64_array()` helper collapse the ~400 batch call sites; `array.array` is built via `bytemuck` (Zlib/MIT/Apache — `cargo deny check licenses` ok).
- Tests migrated to `array.array`/`Matrix` (1-D `.shape`→`len()`, `.dtype`→`.typecode`; numeric comparisons normalize through a `_to_np` helper). The non-contiguous-input test now asserts acceptance instead of rejection.

## Verification (local)
- `pytest bindings/python/tests` — **1991 passed**.
- `cargo fmt --all`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, `cargo test --workspace --all-features`, `cargo deny check licenses` — all green.

**BREAKING for Python**: batch return types change from NumPy arrays to `array.array`/`Matrix`. Documented in CHANGELOG; ships with the data-layer release bundle (no separate tag).
This commit is contained in:
kingchenc
2026-06-17 03:19:50 +02:00
committed by GitHub
parent 677ea37402
commit 8228be7069
12 changed files with 1910 additions and 2713 deletions
@@ -8,20 +8,22 @@ import pytest
import wickra as ta
def test_non_contiguous_array_raises_value_error():
# A strided view is not C-contiguous; batch() must reject it cleanly.
def test_non_contiguous_array_is_accepted():
# Inputs are read through the buffer/sequence protocol, so a strided (non
# C-contiguous) view is accepted and yields the same result as a dense copy.
base = np.linspace(1.0, 100.0, 60)
non_contiguous = base[::2]
assert not non_contiguous.flags["C_CONTIGUOUS"]
with pytest.raises(ValueError):
ta.SMA(5).batch(non_contiguous)
strided = np.asarray(ta.SMA(5).batch(non_contiguous))
dense = np.asarray(ta.SMA(5).batch(np.ascontiguousarray(non_contiguous)))
np.testing.assert_array_equal(strided, dense)
def test_ascontiguousarray_recovers():
def test_dense_array_batches():
base = np.linspace(1.0, 100.0, 60)
fixed = np.ascontiguousarray(base[::2])
out = ta.SMA(5).batch(fixed)
assert out.shape == fixed.shape
assert len(out) == len(fixed)
def test_unequal_length_candle_batch_raises(ohlc_series):