From 7f8d1e53ff370e0aabedf8548406093a3f17b2a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 17:06:24 +0000 Subject: [PATCH] Install sonpipe from a GitHub archive URL instead of git+ pip and uv shell out to a system git for git+ URLs, and many end-user machines (the planned uv-tool-based NDI installer's targets) have no git. Point the [ced] extra and ndr.setup.sonpipe at https://github.com/VH-Lab/sonpipe/archive/refs/heads/main.zip, fetched over plain HTTPS. The constant keeps its SONPIPE_GIT_URL name because the tests import it; add tests that it has no git+ and matches the extra. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017vKVGMMtp468mia5dVhAh5 --- .github/workflows/ci.yml | 8 ++++---- pyproject.toml | 12 +++++++++--- src/ndr/ndr_matlab_python_bridge.yaml | 3 ++- src/ndr/setup/sonpipe.py | 18 +++++++++++++++--- tests/test_setup_sonpipe.py | 20 +++++++++++++++++++- 5 files changed, 49 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84d3240..2bb2812 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,10 +148,10 @@ jobs: python-version: "3.14" - name: Install package with dev and ced dependencies - # The [ced] extra pulls sonpipe from its git URL. Same install as - # `python -m ndr.setup.sonpipe`; downstream packages (NDI-python) use - # the module entry point in their own CI so they don't have to know - # the git URL themselves. + # The [ced] extra pulls sonpipe from its GitHub archive URL (plain + # HTTPS, no git needed). Same install as `python -m ndr.setup.sonpipe`; + # downstream packages (NDI-python) use the module entry point in their + # own CI so they don't have to know the URL themselves. run: pip install -e ".[dev,ced]" - name: Verify sonpipe and sonpy are usable diff --git a/pyproject.toml b/pyproject.toml index 9ed8d69..ee4edd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,11 +55,17 @@ formats = [ # unconditionally would put every install of ndr behind a second repository # and a narrow Python. Opt in with `pip install ndr[ced]`, or run # `python -m ndr.setup.sonpipe` after installing ndr -- both land on the same -# git URL. Without either the CED reader raises SonpipeNotFoundError. -ced = ["sonpipe @ git+https://github.com/VH-Lab/sonpipe.git"] +# URL. Without either the CED reader raises SonpipeNotFoundError. +# The URL is GitHub's zip archive of sonpipe's main branch, fetched over plain +# HTTPS, rather than a git+ URL: pip and uv shell out to a system `git` for +# git+ URLs, and many end-user machines (and installers built on +# `uv tool install`) have none. Archive URLs of a branch are cached, so picking +# up a newer sonpipe needs `--upgrade --force-reinstall` (pip) or +# `--reinstall` (uv). +ced = ["sonpipe @ https://github.com/VH-Lab/sonpipe/archive/refs/heads/main.zip"] [tool.hatch.metadata] -# The [ced] extra points at sonpipe's git URL; PEP 508 direct references +# The [ced] extra points at sonpipe's archive URL; PEP 508 direct references # need this opt-in to appear in optional-dependencies. allow-direct-references = true diff --git a/src/ndr/ndr_matlab_python_bridge.yaml b/src/ndr/ndr_matlab_python_bridge.yaml index 964f4a2..5b1a303 100644 --- a/src/ndr/ndr_matlab_python_bridge.yaml +++ b/src/ndr/ndr_matlab_python_bridge.yaml @@ -12,7 +12,8 @@ project_metadata: # so the CED reader can drive CED's sonpy. Also # available as the [ced] optional extra in # pyproject.toml; both entry points install the -# same git URL. +# same URL (a GitHub archive zip over HTTPS, +# so no system git is needed). # # Not ported (MATLAB-only tooling, no Python counterpart is meaningful): # +ndr/+docs/build.m - builds MATLAB markdown docs diff --git a/src/ndr/setup/sonpipe.py b/src/ndr/setup/sonpipe.py index 4b775ed..ad8e3ea 100644 --- a/src/ndr/setup/sonpipe.py +++ b/src/ndr/setup/sonpipe.py @@ -8,8 +8,16 @@ builds (CPython 3.14 on Linux at the time of writing). Both facts are why this is a runtime step rather than a declared dependency, and why the same install is also exposed as the ``[ced]`` optional extra in pyproject.toml -- -that extra points at the same git URL this module installs, so which entry -point a caller uses does not change what lands. +that extra points at the same URL this module installs, so which entry +point a caller uses does not change what lands. (Keep the two strings +identical: if both forms were installed into one environment with different +URLs, pip and uv would see conflicting direct references.) + +The URL is GitHub's zip archive of sonpipe's main branch rather than a +``git+`` URL, because pip and uv shell out to a system ``git`` for ``git+`` +URLs and many end-user machines have none; the archive is fetched over plain +HTTPS. pip caches a branch archive, so ``--upgrade`` alone may not pick up a +newer sonpipe -- reinstall with ``pip install --upgrade --force-reinstall``. Usage:: @@ -28,7 +36,11 @@ import subprocess import sys -SONPIPE_GIT_URL = "sonpipe @ git+https://github.com/VH-Lab/sonpipe.git" +# Historical name: this was once a git+ URL. It is now a plain-HTTPS archive +# URL so that installing sonpipe does not need git; the name is kept because +# callers (tests, downstream code) import it. Must match the [ced] extra in +# pyproject.toml exactly. +SONPIPE_GIT_URL = "sonpipe @ https://github.com/VH-Lab/sonpipe/archive/refs/heads/main.zip" class SonpipeInstallError(RuntimeError): diff --git a/tests/test_setup_sonpipe.py b/tests/test_setup_sonpipe.py index 2b1a7ba..ba95785 100644 --- a/tests/test_setup_sonpipe.py +++ b/tests/test_setup_sonpipe.py @@ -8,8 +8,10 @@ from __future__ import annotations +import re import subprocess import sys +from pathlib import Path from unittest import mock import pytest @@ -18,7 +20,7 @@ class TestInstall: - def test_invokes_pip_with_the_git_url(self, monkeypatch): + def test_invokes_pip_with_the_sonpipe_url(self, monkeypatch): seen: dict[str, object] = {} def fake_run(argv, **kwargs): @@ -55,6 +57,22 @@ def fake_run(argv, **kwargs): setup_sonpipe.install() +class TestUrl: + def test_url_needs_no_git(self): + # pip and uv shell out to a system git for git+ URLs; end-user + # installers cannot assume one exists. + assert "git+" not in setup_sonpipe.SONPIPE_GIT_URL + assert "https://" in setup_sonpipe.SONPIPE_GIT_URL + + def test_matches_the_ced_extra(self): + # Both entry points must name the same URL, or an environment with + # both would hold conflicting direct references for sonpipe. + pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml" + match = re.search(r"^ced = \[\"(.+?)\"\]$", pyproject.read_text(), re.MULTILINE) + assert match is not None, "[ced] extra not found in pyproject.toml" + assert match.group(1) == setup_sonpipe.SONPIPE_GIT_URL + + class TestCli: def test_check_reports_when_sonpipe_is_reachable(self, monkeypatch, capsys): with mock.patch("ndr.format.ced.sonpipe.executable", return_value=["/opt/sonpipe"]):