From 166935415d760fd307f84b0d32a691b83927f9e4 Mon Sep 17 00:00:00 2001 From: VH-Lab Date: Sat, 18 Jul 2026 18:43:53 +0000 Subject: [PATCH 1/5] install.sh: give shell-aware PATH instructions The PATH warning hard-coded ~/.profile, which zsh (the macOS default since Catalina, including Tahoe) does not read, so the advice silently did nothing for most Mac users. Detect the login shell and print the correct instruction: ~/.zshrc for zsh, ~/.bash_profile (macOS) / ~/.bashrc (Linux) for bash, fish_add_path for fish, with a generic fallback otherwise. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VTQEJGfiSxeVG1J5GSmaTy --- install.sh | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 3aa720f..a02130e 100755 --- a/install.sh +++ b/install.sh @@ -42,6 +42,23 @@ log() { printf '\033[1;34m[sonpipe]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[sonpipe] WARNING:\033[0m %s\n' "$*" >&2; } err() { printf '\033[1;31m[sonpipe] ERROR:\033[0m %s\n' "$*" >&2; exit 1; } +# Print a PATH-setup instruction appropriate to the user's login shell. +path_hint() { + bindir="$1" + case "$(basename "${SHELL:-}")" in + zsh) + warn " echo 'export PATH=\"$bindir:\$PATH\"' >> ~/.zshrc && source ~/.zshrc" ;; + bash) + # macOS login shells read ~/.bash_profile; most Linux shells read ~/.bashrc. + if [ "$(uname -s)" = "Darwin" ]; then rc="~/.bash_profile"; else rc="~/.bashrc"; fi + warn " echo 'export PATH=\"$bindir:\$PATH\"' >> $rc && source $rc" ;; + fish) + warn " fish_add_path $bindir" ;; + *) + warn " add to your shell profile: export PATH=\"$bindir:\$PATH\"" ;; + esac +} + while [ $# -gt 0 ]; do case "$1" in --prefix) PREFIX="$2"; shift 2 ;; @@ -122,8 +139,8 @@ if [ "$SYMLINK" -eq 1 ]; then CMD="$BIN_DIR/sonpipe" case ":$PATH:" in *":$BIN_DIR:"*) : ;; - *) warn "$BIN_DIR is not on your PATH. Add it, e.g.:" - warn " echo 'export PATH=\"$BIN_DIR:\$PATH\"' >> ~/.profile" ;; + *) warn "$BIN_DIR is not on your PATH. Add it with:" + path_hint "$BIN_DIR" ;; esac fi From d605a49f1d77ded18932887c2d2807755a82b8bb Mon Sep 17 00:00:00 2001 From: VH-Lab Date: Sat, 18 Jul 2026 18:53:56 +0000 Subject: [PATCH 2/5] install.sh: make macOS Apple Silicon work automatically CED's macOS sonpy is x86_64-only and linked against the python.org framework build, so it fails to load under uv/Homebrew Python or when run as arm64. The installer now handles this end-to-end on Apple Silicon: - Prefer the python.org framework Python (/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14) when no --python is given on macOS. - Detect Apple Silicon and build + install the venv as x86_64 via `arch -x86_64` (Rosetta); error early with a clear pointer to python.org if the chosen interpreter can't run x86_64. - Install the `sonpipe` command as an `arch -x86_64` wrapper (not a symlink) so it runs x86_64 even when invoked from a native-arm64 host such as MATLAB. - Give a macOS-specific message (pointing at the python.org installer) if sonpy still can't be imported, and verify via load_sonpy() so the check matches how the tool actually resolves sonpy. Document the python.org requirement in the README's Apple Silicon note. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VTQEJGfiSxeVG1J5GSmaTy --- README.md | 19 ++++++++++---- install.sh | 75 ++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8ec1924..0c8eaf4 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,20 @@ installing from PyPI, etc.). > `python3.14` interpreter; if `sonpy` cannot be imported it tells you to > install 3.14 and re-run with `--python "$(command -v python3.14)"`. > -> **Apple Silicon.** CED's current macOS `sonpy` wheel contains an x86_64-only -> binary (despite its `universal2` label), so on Apple Silicon Macs you need an -> **x86_64 Python running under Rosetta 2** until CED ships a native arm64 -> build. Everything else (the sonpipe CLI itself, the MATLAB package, and the -> fake-sonpy test suites) runs natively on arm64. +> **Apple Silicon.** CED's macOS `sonpy` is x86_64-only (despite its +> `universal2` label) **and** is linked against the official **python.org** +> framework build — so it will not load under Homebrew or `uv` Python. On an +> Apple Silicon Mac: +> +> 1. Install **Python 3.14 from [python.org](https://www.python.org/downloads/macos/)** +> (the universal2 installer). +> 2. Run `./install.sh` — it detects Apple Silicon, finds that framework Python +> automatically, builds an **x86_64** venv under Rosetta 2, and installs the +> `sonpipe` command as an `arch -x86_64` wrapper so it runs correctly even +> when launched from MATLAB. +> +> Everything else (the CLI logic, the MATLAB package, and the fake-sonpy test +> suites) runs natively on arm64. ### Manual: pip diff --git a/install.sh b/install.sh index a02130e..0c38b49 100755 --- a/install.sh +++ b/install.sh @@ -76,6 +76,16 @@ done VENV_DIR="${VENV_DIR:-$PREFIX/share/sonpipe/venv}" BIN_DIR="${BIN_DIR:-$PREFIX/bin}" +OS="$(uname -s)" +ARCH="$(uname -m)" +# On Apple Silicon, CED's sonpy is x86_64-only AND is linked against the +# python.org framework build, so we build and run the environment as x86_64 +# (Rosetta) using that framework Python. +MAC_ARM=0 +if [ "$OS" = "Darwin" ] && [ "$ARCH" = "arm64" ]; then MAC_ARM=1; fi +PYRUN="" +[ "$MAC_ARM" -eq 1 ] && PYRUN="arch -x86_64" + # Resolve the directory this script lives in (following symlinks). SOURCE_PATH="${BASH_SOURCE[0]}" while [ -h "$SOURCE_PATH" ]; do @@ -87,15 +97,34 @@ SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" >/dev/null 2>&1 && pwd)" # Pick a Python interpreter (prefer 3.14 for sonpy wheel availability). if [ -z "$PYTHON" ]; then - for c in python3.14 python3 python; do + candidates="python3.14 python3 python" + # On macOS, prefer the python.org framework build -- CED's sonpy is linked + # against it and will not load under other distributions (uv, Homebrew, ...). + if [ "$OS" = "Darwin" ]; then + candidates="/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14 $candidates" + fi + for c in $candidates; do if command -v "$c" >/dev/null 2>&1; then PYTHON="$c"; break; fi done fi [ -n "$PYTHON" ] || err "no Python interpreter found; install Python 3.14 and retry." command -v "$PYTHON" >/dev/null 2>&1 || err "python not found: $PYTHON" -PYVER="$("$PYTHON" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" -log "Using Python $PYVER ($("$PYTHON" -c 'import sys; print(sys.executable)'))" +# On Apple Silicon the interpreter must be able to run as x86_64 (i.e. a +# universal2 build such as the python.org installer). Homebrew/arm64-only +# builds cannot, and uv's standalone build lacks the framework sonpy needs. +if [ "$MAC_ARM" -eq 1 ] && ! $PYRUN "$PYTHON" -c 'pass' >/dev/null 2>&1; then + warn "The chosen Python cannot run as x86_64 (it looks arm64-only)." + warn "On Apple Silicon, install the python.org universal2 build of Python 3.14:" + warn " https://www.python.org/downloads/macos/" + warn "then re-run (the installer will find it automatically), or pass it explicitly:" + warn " ./install.sh --python /Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14" + exit 1 +fi + +PYVER="$($PYRUN "$PYTHON" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" +log "Using Python $PYVER ($($PYRUN "$PYTHON" -c 'import sys; print(sys.executable)'))" +[ "$MAC_ARM" -eq 1 ] && log "Apple Silicon: building an x86_64 environment via Rosetta (arch -x86_64)." if [ "$PYVER" != "3.14" ]; then warn "CED sonpy ships Linux/macOS wheels only for Python 3.14; you are on $PYVER." warn "If sonpy fails to import below, re-run with: --python \"\$(command -v python3.14)\"" @@ -109,33 +138,53 @@ log "Installing from: $SOURCE" log "Creating virtual environment: $VENV_DIR" mkdir -p "$(dirname "$VENV_DIR")" -"$PYTHON" -m venv "$VENV_DIR" +$PYRUN "$PYTHON" -m venv "$VENV_DIR" PY="$VENV_DIR/bin/python" log "Upgrading pip" -"$PY" -m pip install --upgrade pip >/dev/null +$PYRUN "$PY" -m pip install --upgrade pip >/dev/null log "Installing sonpipe (also fetches CED's sonpy from PyPI)" -"$PY" -m pip install "$SOURCE" +$PYRUN "$PY" -m pip install "$SOURCE" # Verify. log "Verifying sonpipe CLI ..." -"$PY" -m sonpipe --version || err "sonpipe did not install correctly." +$PYRUN "$PY" -m sonpipe --version || err "sonpipe did not install correctly." log "Verifying CED sonpy import ..." SONPY_OK=1 -if ! "$PY" -c "import sonpy; print('sonpy', sonpy.__version__)"; then +if ! $PYRUN "$PY" -c "from sonpipe.sonfile import load_sonpy; load_sonpy()"; then SONPY_OK=0 - warn "sonpy could not be imported -- most likely no sonpy wheel exists for" - warn "Python $PYVER on this platform. Install Python 3.14 and re-run:" - warn " ./install.sh --python \"\$(command -v python3.14)\"" + if [ "$OS" = "Darwin" ]; then + warn "sonpy could not be imported. On macOS, CED's sonpy is x86_64-only and is" + warn "linked against the python.org framework Python. Install Python 3.14 from" + warn " https://www.python.org/downloads/macos/" + warn "and re-run (it will be found automatically), or pass it explicitly:" + warn " ./install.sh --python /Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14" + else + warn "sonpy could not be imported -- most likely no sonpy wheel exists for" + warn "Python $PYVER on this platform. Install Python 3.14 and re-run:" + warn " ./install.sh --python \"\$(command -v python3.14)\"" + fi fi -# Symlink the command onto the PATH. +# Put the command on the PATH. CMD="$VENV_DIR/bin/sonpipe" if [ "$SYMLINK" -eq 1 ]; then mkdir -p "$BIN_DIR" - ln -sf "$CMD" "$BIN_DIR/sonpipe" + if [ "$MAC_ARM" -eq 1 ]; then + # A wrapper (not a symlink) so the command always runs as x86_64 under + # Rosetta -- including when launched from a native-arm64 host like MATLAB, + # where a symlink's shebang would run arm64 and fail to load sonpy. + rm -f "$BIN_DIR/sonpipe" + cat > "$BIN_DIR/sonpipe" < Date: Sat, 18 Jul 2026 18:59:13 +0000 Subject: [PATCH 3/5] Expand leading ~ in file paths Callers such as MATLAB pass paths like '~/Desktop/x.smrx' through literally, and neither the shell (the arg is quoted) nor Python expands the tilde, so the file was reported missing. SmrxFile now runs os.path.expanduser on the path. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VTQEJGfiSxeVG1J5GSmaTy --- src/sonpipe/sonfile.py | 4 +++- tests/test_sonfile.py | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sonpipe/sonfile.py b/src/sonpipe/sonfile.py index 967c14e..d08b4b2 100644 --- a/src/sonpipe/sonfile.py +++ b/src/sonpipe/sonfile.py @@ -96,7 +96,9 @@ def __init__(self, path, sonlib=None): if sonlib is None: sonlib = load_sonpy() self._son = sonlib - self.path = os.fspath(path) + # Expand a leading ~ (callers such as MATLAB pass it through literally, + # and neither the shell nor Python expands it inside a quoted argument). + self.path = os.path.expanduser(os.fspath(path)) if not os.path.exists(self.path): raise SonpipeError("File not found: {}".format(self.path)) diff --git a/tests/test_sonfile.py b/tests/test_sonfile.py index 02a1bd3..cd367b4 100644 --- a/tests/test_sonfile.py +++ b/tests/test_sonfile.py @@ -68,6 +68,15 @@ def test_missing_file_raises(): open_file("/no/such/file.smrx") +def test_leading_tilde_is_expanded(tmp_path, monkeypatch): + # HOME (Unix) / USERPROFILE (Windows) drive os.path.expanduser. + monkeypatch.setenv("HOME", str(tmp_path)) + monkeypatch.setenv("USERPROFILE", str(tmp_path)) + (tmp_path / "ex.smrx").write_bytes(b"") + smrx = open_file("~/ex.smrx") + assert smrx.path == str(tmp_path / "ex.smrx") + + def test_channel_numbers_skip_off(smrx_path): smrx = open_file(smrx_path) # slot index 1 (Spike2 #2) is Off and must be omitted. From fbbbc6a357834b1f5b73a3c8c2f754d24c7573c6 Mon Sep 17 00:00:00 2001 From: VH-Lab Date: Sat, 18 Jul 2026 19:07:22 +0000 Subject: [PATCH 4/5] install.sh: idempotent install/update; add update.sh Re-running install.sh now reuses an existing environment and upgrades the sonpipe package in place (fast; no venv rebuild, no sonpy re-download) instead of failing or rebuilding. The update path skips base-Python discovery entirely (the venv already has its interpreter), so `./install.sh` with no args works as the updater. Add --recreate for a clean rebuild and --update as an explicit alias, and a thin update.sh wrapper for discoverability. Also fix a set -u crash: PYVER was unset on the reuse path (used in the sonpy-import-failed message); it is now derived from the venv's Python and always defined. Document updating in the README. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VTQEJGfiSxeVG1J5GSmaTy --- README.md | 5 +++ install.sh | 116 ++++++++++++++++++++++++++++++++++------------------- update.sh | 16 ++++++++ 3 files changed, 95 insertions(+), 42 deletions(-) create mode 100755 update.sh diff --git a/README.md b/README.md index 0c8eaf4..86b45bd 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ the command at `~/.local/bin/sonpipe`. On Windows the venv lives under paste into MATLAB. Run `./install.sh --help` for options (custom prefix, Python, installing from PyPI, etc.). +**Updating.** After a `git pull`, re-run `./install.sh` (or `./update.sh`) — it +reuses the existing environment and upgrades the sonpipe package in place, which +is fast and doesn't re-download `sonpy`. Use `./install.sh --recreate` to force +a clean rebuild. + > **Python version.** CED ships `sonpy` wheels for **Python 3.14 on Linux and > macOS** (and Python 3.9–3.14 on Windows). The installer prefers a > `python3.14` interpreter; if `sonpy` cannot be imported it tells you to diff --git a/install.sh b/install.sh index 0c38b49..81931f0 100755 --- a/install.sh +++ b/install.sh @@ -3,26 +3,33 @@ # sonpipe installer for Linux and macOS. # # Installs the sonpipe command-line tool (and CED's sonpy) into an isolated -# Python virtual environment, and symlinks the `sonpipe` command into your +# Python virtual environment, and puts the `sonpipe` command in your # ~/.local/bin so it is on your PATH. Then verifies the install and prints the # line you need to wire sonpipe into MATLAB. # +# Re-run it any time to UPDATE: if the environment already exists it is reused +# and the sonpipe package is upgraded in place (fast; no venv rebuild). Use +# --recreate to force a clean rebuild. +# # venv: ~/.local/share/sonpipe/venv (isolated; holds sonpy + numpy) -# command: ~/.local/bin/sonpipe (symlink onto your PATH) +# command: ~/.local/bin/sonpipe (onto your PATH) # # Usage: -# ./install.sh [options] +# ./install.sh [options] # install, or update if already installed # # Options: # --prefix DIR Base install prefix (default: ~/.local); the venv goes in # DIR/share/sonpipe/venv and the command in DIR/bin. # --venv DIR Override the virtual environment location. -# --bin-dir DIR Override where the `sonpipe` command is linked. +# --bin-dir DIR Override where the `sonpipe` command is placed. # --python PY Python interpreter to build the venv with (default: auto, # prefers python3.14 for sonpy wheel availability). # --pypi Install from PyPI (default when not run inside the repo). # --source PATH Install from this path or pip spec (default: the repo). -# --no-symlink Do not create the ~/.local/bin symlink. +# --update Update the existing environment in place (the default when +# one exists). +# --recreate Delete and rebuild the environment from scratch. +# --no-symlink Do not create the ~/.local/bin command. # -h, --help Show this help. # # Note: CED ships sonpy Linux/macOS wheels only for Python 3.14, so this script @@ -37,6 +44,7 @@ BIN_DIR="" PYTHON="" SOURCE="" SYMLINK=1 +RECREATE=0 log() { printf '\033[1;34m[sonpipe]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[sonpipe] WARNING:\033[0m %s\n' "$*" >&2; } @@ -68,7 +76,9 @@ while [ $# -gt 0 ]; do --source) SOURCE="$2"; shift 2 ;; --pypi) SOURCE="sonpipe"; shift ;; --no-symlink) SYMLINK=0; shift ;; - -h|--help) sed -n '2,38p' "$0" | sed 's/^#\{0,1\} \{0,1\}//'; exit 0 ;; + --recreate) RECREATE=1; shift ;; + --update) RECREATE=0; shift ;; + -h|--help) sed -n '2,37p' "$0" | sed 's/^#\{0,1\} \{0,1\}//'; exit 0 ;; *) err "unknown option: $1 (use --help)" ;; esac done @@ -95,39 +105,52 @@ while [ -h "$SOURCE_PATH" ]; do done SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" >/dev/null 2>&1 && pwd)" -# Pick a Python interpreter (prefer 3.14 for sonpy wheel availability). -if [ -z "$PYTHON" ]; then - candidates="python3.14 python3 python" - # On macOS, prefer the python.org framework build -- CED's sonpy is linked - # against it and will not load under other distributions (uv, Homebrew, ...). - if [ "$OS" = "Darwin" ]; then - candidates="/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14 $candidates" - fi - for c in $candidates; do - if command -v "$c" >/dev/null 2>&1; then PYTHON="$c"; break; fi - done -fi -[ -n "$PYTHON" ] || err "no Python interpreter found; install Python 3.14 and retry." -command -v "$PYTHON" >/dev/null 2>&1 || err "python not found: $PYTHON" - -# On Apple Silicon the interpreter must be able to run as x86_64 (i.e. a -# universal2 build such as the python.org installer). Homebrew/arm64-only -# builds cannot, and uv's standalone build lacks the framework sonpy needs. -if [ "$MAC_ARM" -eq 1 ] && ! $PYRUN "$PYTHON" -c 'pass' >/dev/null 2>&1; then - warn "The chosen Python cannot run as x86_64 (it looks arm64-only)." - warn "On Apple Silicon, install the python.org universal2 build of Python 3.14:" - warn " https://www.python.org/downloads/macos/" - warn "then re-run (the installer will find it automatically), or pass it explicitly:" - warn " ./install.sh --python /Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14" - exit 1 +# --recreate wipes any existing environment so we build fresh. +if [ "$RECREATE" -eq 1 ] && [ -d "$VENV_DIR" ]; then + log "Removing existing environment (--recreate): $VENV_DIR" + rm -rf "$VENV_DIR" fi +# Re-running over an existing venv is a fast in-place update. +REUSE=0 +[ -f "$VENV_DIR/pyvenv.cfg" ] && REUSE=1 + +# A base interpreter is only needed to *create* a venv. Updating an existing one +# reuses its interpreter, so skip Python discovery/validation entirely. +if [ "$REUSE" -eq 0 ]; then + # Pick a Python interpreter (prefer 3.14 for sonpy wheel availability). + if [ -z "$PYTHON" ]; then + candidates="python3.14 python3 python" + # On macOS, prefer the python.org framework build -- CED's sonpy is linked + # against it and will not load under other distributions (uv, Homebrew, ...). + if [ "$OS" = "Darwin" ]; then + candidates="/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14 $candidates" + fi + for c in $candidates; do + if command -v "$c" >/dev/null 2>&1; then PYTHON="$c"; break; fi + done + fi + [ -n "$PYTHON" ] || err "no Python interpreter found; install Python 3.14 and retry." + command -v "$PYTHON" >/dev/null 2>&1 || err "python not found: $PYTHON" + + # On Apple Silicon the interpreter must be able to run as x86_64 (i.e. a + # universal2 build such as the python.org installer). Homebrew/arm64-only + # builds cannot, and uv's standalone build lacks the framework sonpy needs. + if [ "$MAC_ARM" -eq 1 ] && ! $PYRUN "$PYTHON" -c 'pass' >/dev/null 2>&1; then + warn "The chosen Python cannot run as x86_64 (it looks arm64-only)." + warn "On Apple Silicon, install the python.org universal2 build of Python 3.14:" + warn " https://www.python.org/downloads/macos/" + warn "then re-run (the installer will find it automatically), or pass it explicitly:" + warn " ./install.sh --python /Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14" + exit 1 + fi -PYVER="$($PYRUN "$PYTHON" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" -log "Using Python $PYVER ($($PYRUN "$PYTHON" -c 'import sys; print(sys.executable)'))" -[ "$MAC_ARM" -eq 1 ] && log "Apple Silicon: building an x86_64 environment via Rosetta (arch -x86_64)." -if [ "$PYVER" != "3.14" ]; then - warn "CED sonpy ships Linux/macOS wheels only for Python 3.14; you are on $PYVER." - warn "If sonpy fails to import below, re-run with: --python \"\$(command -v python3.14)\"" + PYVER="$($PYRUN "$PYTHON" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" + log "Using Python $PYVER ($($PYRUN "$PYTHON" -c 'import sys; print(sys.executable)'))" + [ "$MAC_ARM" -eq 1 ] && log "Apple Silicon: building an x86_64 environment via Rosetta (arch -x86_64)." + if [ "$PYVER" != "3.14" ]; then + warn "CED sonpy ships Linux/macOS wheels only for Python 3.14; you are on $PYVER." + warn "If sonpy fails to import below, re-run with: --python \"\$(command -v python3.14)\"" + fi fi # Decide what to install. @@ -136,16 +159,25 @@ if [ -z "$SOURCE" ]; then fi log "Installing from: $SOURCE" -log "Creating virtual environment: $VENV_DIR" -mkdir -p "$(dirname "$VENV_DIR")" -$PYRUN "$PYTHON" -m venv "$VENV_DIR" +if [ "$REUSE" -eq 1 ]; then + log "Updating existing environment: $VENV_DIR (use --recreate for a clean rebuild)" +else + log "Creating virtual environment: $VENV_DIR" + mkdir -p "$(dirname "$VENV_DIR")" + $PYRUN "$PYTHON" -m venv "$VENV_DIR" +fi PY="$VENV_DIR/bin/python" +# Version of the (possibly pre-existing) environment; used in messages below and +# always defined, including on the reuse/update path. +PYVER="$($PYRUN "$PY" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" log "Upgrading pip" $PYRUN "$PY" -m pip install --upgrade pip >/dev/null -log "Installing sonpipe (also fetches CED's sonpy from PyPI)" -$PYRUN "$PY" -m pip install "$SOURCE" +# --upgrade updates sonpipe in place; dependencies (sonpy, numpy) are only +# touched if a version constraint requires it, so re-running is a fast update. +log "Installing/updating sonpipe (fetches CED's sonpy from PyPI if needed)" +$PYRUN "$PY" -m pip install --upgrade "$SOURCE" # Verify. log "Verifying sonpipe CLI ..." diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..f9d4cab --- /dev/null +++ b/update.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# +# sonpipe updater for Linux and macOS. +# +# Convenience wrapper around install.sh: updates the sonpipe package in your +# existing environment in place (no venv rebuild, no re-download of sonpy). +# All arguments are forwarded to install.sh, so e.g. `./update.sh --recreate` +# forces a clean rebuild instead. +# +# Typical use after a `git pull`: +# ./update.sh + +set -euo pipefail + +DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +exec "$DIR/install.sh" --update "$@" From 7791b60ddc8053c346658c3f7b2299215dbcc39a Mon Sep 17 00:00:00 2001 From: VH-Lab Date: Sat, 18 Jul 2026 19:13:17 +0000 Subject: [PATCH 5/5] Fix Windows-only failure in tilde-expansion test os.path.expanduser('~/ex.smrx') returns a path with a forward slash in the tail on Windows, so the exact string compare against str(tmp_path/'ex.smrx') (backslash) failed there. Compare with os.path.normpath so the assertion is separator-agnostic. Product behaviour was already correct. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VTQEJGfiSxeVG1J5GSmaTy --- tests/test_sonfile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_sonfile.py b/tests/test_sonfile.py index cd367b4..dde1727 100644 --- a/tests/test_sonfile.py +++ b/tests/test_sonfile.py @@ -1,5 +1,6 @@ """Tests for the SmrxFile wrapper and channel mappings.""" +import os import types import numpy as np @@ -74,7 +75,8 @@ def test_leading_tilde_is_expanded(tmp_path, monkeypatch): monkeypatch.setenv("USERPROFILE", str(tmp_path)) (tmp_path / "ex.smrx").write_bytes(b"") smrx = open_file("~/ex.smrx") - assert smrx.path == str(tmp_path / "ex.smrx") + # normpath so the comparison is separator-agnostic (Windows mixes / and \). + assert os.path.normpath(smrx.path) == os.path.normpath(str(tmp_path / "ex.smrx")) def test_channel_numbers_skip_off(smrx_path):