diff --git a/crates/hiroz-py/pyproject.toml b/crates/hiroz-py/pyproject.toml index 8aedb5298..b251d7e9c 100644 --- a/crates/hiroz-py/pyproject.toml +++ b/crates/hiroz-py/pyproject.toml @@ -27,6 +27,13 @@ dependencies = [ "hiroz-msgs-py>=0.1.0", ] +[project.optional-dependencies] +# numpy backs the tests that prove it can consume ZPayloadView's exported +# buffer without adding a copy. Declared here rather than installed +# imperatively by the test script so that `pip install -e ".[test]"` is a +# true instruction -- the tests' own failure message points at it. +test = ["numpy>=1.21"] + [tool.maturin] features = ["pyo3/extension-module", "jazzy"] python-source = "python" diff --git a/crates/hiroz-py/tests/test_payload_view.py b/crates/hiroz-py/tests/test_payload_view.py index 8a814e4dc..6b9a2ee31 100644 --- a/crates/hiroz-py/tests/test_payload_view.py +++ b/crates/hiroz-py/tests/test_payload_view.py @@ -163,12 +163,32 @@ def test_recv_raw_view_timeout(self, byte_array_pubsub): class TestZPayloadViewNumpy: - """Test ZPayloadView with numpy (if available).""" + """Test that numpy can consume ZPayloadView without adding a copy. + + numpy is a declared test dependency, so its absence is a failure rather + than a skip. These tests previously guarded with ``importorskip`` and + numpy was never installed, so both had *always* skipped. A permanently + skipped test is indistinguishable from a passing one in the summary line. + + Scope, precisely: ``arr.flags["OWNDATA"] is False`` proves numpy borrowed + the buffer ZPayloadView exported rather than copying it. It does *not* + prove the transport was zero-copy — ``ZPayloadView::new`` falls back to + ``PayloadBytes::Owned`` for a fragmented payload, and numpy borrows that + owned copy just as happily. ``test_payload_view_is_zero_copy`` asserts + ``payload.is_zero_copy_py``, which is the transport-level check. + """ @pytest.fixture(autouse=True) def check_numpy(self): - """Skip tests if numpy is not available.""" - pytest.importorskip("numpy") + """Fail loudly if numpy is missing, rather than skipping silently.""" + try: + import numpy # noqa: F401 + except ImportError: # pragma: no cover - env misconfiguration + pytest.fail( + "numpy is a declared test dependency but is not installed, so " + "the ZPayloadView zero-copy assertions cannot run. Install it " + "with the test extra; do not restore importorskip here." + ) def test_numpy_frombuffer(self, byte_array_pubsub): """Test that numpy.frombuffer works with ZPayloadView.""" diff --git a/scripts/test-python.nu b/scripts/test-python.nu index 7a21507af..cd41bc45d 100755 --- a/scripts/test-python.nu +++ b/scripts/test-python.nu @@ -42,6 +42,14 @@ def setup-venv [] { run-cmd "cd crates/hiroz-py; source .venv/bin/activate && pip install -e ../hiroz-msgs/python/" --shell bash --distro (get-distro) print " Installed hiroz-msgs-py (message types)" + # Test-only dependency. `TestZPayloadViewNumpy` checks that numpy consumes + # ZPayloadView's exported buffer without adding a copy, and the venv is + # created with plain `python -m venv`, so it inherits nothing from the + # surrounding devShell. Without this the tests skip and the suite still + # reports success -- see #266. + run-cmd "cd crates/hiroz-py; source .venv/bin/activate && pip install -e '.[test]'" --shell bash --distro (get-distro) + print " Installed the test extra (numpy, for the zero-copy assertions)" + # Install hiroz-py in editable mode using maturin run-cmd "cd crates/hiroz-py; source .venv/bin/activate && RUSTFLAGS='-D warnings' maturin develop" --shell bash --distro (get-distro) print " Installed hiroz-py (Rust bindings)"