diff --git a/.github/workflows/typedpython.yml b/.github/workflows/typedpython.yml index 8b26b70d..b49549e6 100644 --- a/.github/workflows/typedpython.yml +++ b/.github/workflows/typedpython.yml @@ -34,7 +34,7 @@ jobs: # pyrefly is pinned exactly: the gate reads its versioned type-report format. run: | uv venv .venv --python 3.15t - uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest + uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest numpy - name: Behaviour tests run: .venv/bin/python -m pytest -q -m "not measurement and not race_mutant" @@ -62,7 +62,7 @@ jobs: - name: Create the environment run: | uv venv .venv --python 3.15t - uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest + uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest numpy - name: Relax ASLR entropy (ASan shadow mapping on newer kernels) run: sudo sysctl -w vm.mmap_rnd_bits=28 || true @@ -103,7 +103,7 @@ jobs: working-directory: python-multiplatform-ksp run: | uv venv .venv --python 3.15t - uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest + uv pip install --python .venv/bin/python "pyrefly==1.3.2" pytest numpy - name: App fixture tests run: | diff --git a/python-multiplatform-ksp/src/test/python/test_buffer_arrays.py b/python-multiplatform-ksp/src/test/python/test_buffer_arrays.py index b1d0246a..6fa1e44c 100644 --- a/python-multiplatform-ksp/src/test/python/test_buffer_arrays.py +++ b/python-multiplatform-ksp/src/test/python/test_buffer_arrays.py @@ -24,7 +24,6 @@ import importlib.util import textwrap -import numpy as np import pytest from typedpython import cbuild, cgen, frontend, ir, verify @@ -32,8 +31,6 @@ SOURCE = textwrap.dedent(''' # typedpython: compiled import array - import numpy as np - import numpy.typing as npt def sum_ad(a: array.array[float]) -> float: @@ -119,22 +116,13 @@ def rebind_view(a: array.array[float]) -> None: y[0] = 5.0 - def nd_first(x: npt.NDArray[np.float64]) -> object: - return x[0] - - - def nd_sum(x: npt.NDArray[np.float64]) -> object: - s = 0.0 - for i in range(len(x)): - s += x[i] - return s ''') COMPILED = { "sum_ad", "sum_aq", "get_ad", "get_aq", "set_ad", "set_aq", "length_ad", "scale_ad", "iota_aq", "copy_ad", "cascade_ad", "fill_n", "mixed", "four", "div_ad", } -SKIPPED = {"open_ad", "rebind_view", "nd_first", "nd_sum"} +SKIPPED = {"open_ad", "rebind_view"} def build_source(d, source, name="bufmod"): @@ -252,23 +240,6 @@ def wrapper_text(c, name): return c[start:c.index("\n}\n", start)] -# --- numpy is not supported (#220): an ordinary object parameter ---------------------------------- - -def test_numpy_parameter_is_an_ordinary_object_and_keeps_numpy_results(built): - for f in built.proved.functions: - assert all(not isinstance(p, ir.ArrayParam) or p.buffer == "array" or p.buffer is None - for p in f.params) - assert not (f.name.startswith("nd_") and any(isinstance(p, ir.ArrayParam) for p in f.params)) - x = np.array(F, dtype=np.float64) - r = built.module.nd_first(x) - assert type(r) is np.float64 and r == F[0] - r = built.module.nd_sum(x) # never compiled: the module's plain function - ref = 0.0 - for v in x: - ref += v - assert type(r) is np.float64 and r == ref - - # --- reads, writes, len --------------------------------------------------------------------------- @pytest.mark.parametrize("name,make", [ @@ -375,7 +346,6 @@ class SubArray(array.array): ("sum_ad", lambda: (array.array("f", [1.0, 2.0]),)), ("sum_ad", lambda: (SubArray("d", F),)), ("sum_ad", lambda: (list(F),)), - ("sum_ad", lambda: (np.array(F),)), ("sum_ad", lambda: (memoryview(AD()),)), ("sum_ad", lambda: (None,)), ]) diff --git a/python-multiplatform-ksp/src/test/python/test_buffer_numpy.py b/python-multiplatform-ksp/src/test/python/test_buffer_numpy.py new file mode 100644 index 00000000..b03d49a8 --- /dev/null +++ b/python-multiplatform-ksp/src/test/python/test_buffer_numpy.py @@ -0,0 +1,64 @@ +"""numpy arrays are not compiled yet (#220): an `NDArray[...]` parameter is an ordinary object +parameter, so results and their numpy scalar types equal the interpreter's. Skipped without numpy.""" +import textwrap + +import pytest + +np = pytest.importorskip("numpy") + +from typedpython import cbuild, ir # noqa: E402 +from test_buffer_arrays import AD, F, build_source, differential, load_so # noqa: E402 + +SOURCE = textwrap.dedent(''' + # typedpython: compiled + import array + import numpy as np + import numpy.typing as npt + + + def sum_ad(a: array.array[float]) -> float: + s = 0.0 + for i in range(len(a)): + s += a[i] + return s + + + def nd_first(x: npt.NDArray[np.float64]) -> object: + return x[0] + + + def nd_sum(x: npt.NDArray[np.float64]) -> object: + s = 0.0 + for i in range(len(x)): + s += x[i] + return s +''') + + +@pytest.fixture(scope="module") +def built(tmp_path_factory): + d = tmp_path_factory.mktemp("bufnp") + path, proved, diags, c = build_source(d, SOURCE) + so = cbuild.build(c, path.stem, d / "out") + return proved, load_so(so) + + +def test_numpy_parameter_is_an_ordinary_object_and_keeps_numpy_results(built): + proved, m = built + for f in proved.functions: + assert all(not isinstance(p, ir.ArrayParam) or p.buffer == "array" or p.buffer is None + for p in f.params) + assert not (f.name.startswith("nd_") and any(isinstance(p, ir.ArrayParam) for p in f.params)) + x = np.array(F, dtype=np.float64) + r = m.nd_first(x) + assert type(r) is np.float64 and r == F[0] + r = m.nd_sum(x) + ref = 0.0 + for v in x: + ref += v + assert type(r) is np.float64 and r == ref + + +def test_a_numpy_array_given_to_an_array_parameter_takes_the_object_path(built): + _, m = built + differential(m, "sum_ad", lambda: (np.array(F),), expect_deopt=True)