From 399e0bf68af48c3ed4f7f49dfebec4dba3a78245 Mon Sep 17 00:00:00 2001 From: James Gilbert Date: Tue, 27 Jan 2026 18:09:27 +0000 Subject: [PATCH 1/4] Check subprocess exit and tidy up command line generation --- src/lib/PloidyList.c | 2 +- src/lib/PloidyPlot.c | 2 +- src/smudgeplot/cli.py | 90 +++++++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/lib/PloidyList.c b/src/lib/PloidyList.c index 6f1d9e7..55410fa 100644 --- a/src/lib/PloidyList.c +++ b/src/lib/PloidyList.c @@ -1222,7 +1222,7 @@ int main(int argc, char *argv[]) int flags[128]; char *eptr; - ARG_INIT("PloidyList"); + ARG_INIT("extract_kmer_pairs"); OUT = NULL; ETHRESH = 4; diff --git a/src/lib/PloidyPlot.c b/src/lib/PloidyPlot.c index 298ddd3..34953cd 100644 --- a/src/lib/PloidyPlot.c +++ b/src/lib/PloidyPlot.c @@ -1247,7 +1247,7 @@ int main(int argc, char *argv[]) int flags[128]; char *eptr; - ARG_INIT("PloidyPlot"); + ARG_INIT("hetmers"); OUT = NULL; ETHRESH = 4; diff --git a/src/smudgeplot/cli.py b/src/smudgeplot/cli.py index 8eebf1b..b12a4e1 100755 --- a/src/smudgeplot/cli.py +++ b/src/smudgeplot/cli.py @@ -2,10 +2,13 @@ import argparse import os +import shlex import shutil +import subprocess import sys from importlib.metadata import version from pathlib import Path +from typing import Any import numpy as np @@ -41,30 +44,32 @@ def get_binary_path(name: str) -> str: if system_binary: return system_binary - raise FileNotFoundError( - f"Binary '{name}' not found. Please ensure smudgeplot is properly installed. " + msg = ( + f"Binary '{name}' not found. Please ensure smudgeplot is properly installed.\n" f"Checked locations:\n" - f" - Package: {bundled_binary}\n" - f" - System PATH: (not found)\n" + f" - Package: {bundled_binary.parent}\n" + f" - System PATH: {os.get_exec_path()}\n" f"\nYou may need to reinstall smudgeplot or install the binaries manually." ) + raise FileNotFoundError(msg) -def run_binary(name: str, args: str) -> int: +def run_binary(name: str, args: list[Any]) -> None: """ Run a binary with the given arguments. Args: name: Name of the binary - args: Space-separated argument string + args: List of (stringify-able) arguments - Returns: - Return code from the binary + Throws: + subprocess.CalledProcessError on non-zero exit of the command """ - binary_path = get_binary_path(name) - cmd = f"{binary_path} {args}" - sys.stderr.write(f"Calling: {name} {args}\n") - return os.system(cmd) + cmd_line = [get_binary_path(name)] + for x in args: + cmd_line.append(str(x)) + sys.stderr.write(f"Calling: {shlex.join(cmd_line)}\n") + subprocess.run(cmd_line, check=True) class Parser: @@ -332,41 +337,38 @@ def main(): fin() if _parser.task == "hetmers": - # PloidyPlot is expected to be installed in the system as well as the R library supporting it - plot_args = " -o" + str(args.o) - plot_args += " -e" + str(args.L) - plot_args += " -T" + str(args.t) + hetmer_args = [ + f"-o{args.o}", + f"-e{args.L}", + f"-T{args.t}", + ] if args.verbose: - plot_args += " -v" + hetmer_args.append("-v") if args.tmp != ".": - plot_args += " -P" + args.tmp - plot_args += " " + args.infile + hetmer_args.append(f"-P{args.tmp}") + hetmer_args.append(args.infile) - run_binary("hetmers", plot_args) + run_binary("hetmers", hetmer_args) fin() if _parser.task == "extract": - plot_args = " -o" + str(args.o) - plot_args += " -T" + str(args.t) + extract_args = [ + f"-o{args.o}", + f"-T{args.t}", + ] if args.verbose: - plot_args += " -v" + extract_args.append("-v") if args.tmp != ".": - plot_args += " -P" + args.tmp - plot_args += " " + args.infile - if args.sma.endswith(".sma"): - plot_args += " " + args.sma.removesuffix(".sma") - else: - plot_args += " " + args.sma + extract_args.append(f"-P{args.tmp}") + extract_args.append(args.infile) + extract_args.append(args.sma.removesuffix(".sma")) - run_binary("extract_kmer_pairs", plot_args) + run_binary("extract_kmer_pairs", extract_args) fin() - if args.title: - title=args.title - else: - title = ".".join(args.infile.split("/")[-1].split(".")[0:2]) + title = args.title or str(Path(args.infile).with_suffix("").name) if _parser.task == "plot": smudge_tab = smg.read_csv(args.smudgefile, sep="\t", names=["structure", "size", "rel_size"]) @@ -396,12 +398,11 @@ def main(): coverages.local_aggregation(distance=args.d, noise_filter=1000, mask_errors=True) coverages.count_kmers() sys.stderr.write( - f"\t\ - Total kmers: {coverages.total_kmers}\n\t \ - Genomic kmers: {coverages.total_genomic_kmers}\n\t \ - Genomic kmers in smudges: {coverages.total_genomic_kmers_in_smudges}\n\t \ - Sequencing errors: {coverages.total_error_kmers}\n\t \ - Fraction of errors: {round(coverages.total_error_kmers/coverages.total_kmers, 3)}" + f"\nTotal kmers: {coverages.total_kmers}\n" + f"Genomic kmers: {coverages.total_genomic_kmers}\n" + f"Genomic kmers in smudges: {coverages.total_genomic_kmers_in_smudges}\n" + f"Sequencing errors: {coverages.total_error_kmers}\n" + f"Fraction of errors: {coverages.error_fraction:.3f}\n" ) smudge_size_cutoff = ( @@ -420,11 +421,7 @@ def main(): delimiter="\t", ) - limit = 0.7 - if coverages.error_fraction < limit: - cov = smudges.cov - else: - cov = 0 + cov = smudges.cov if coverages.error_fraction < 0.7 else 0 sys.stderr.write("\nCreating centrality plot\n") smudges.centrality_plot(args.o, args.format) @@ -459,10 +456,11 @@ def main(): json_report=args.json_report, input_params=vars(args), palette=args.col_ramp, - invert_cols=args.invert_cols + invert_cols=args.invert_cols, ) fin() + if __name__ == "__main__": main() From 0b0c5178fb11ea46bec8a268e216e9ababee954a Mon Sep 17 00:00:00 2001 From: James Gilbert Date: Wed, 28 Jan 2026 12:04:14 +0000 Subject: [PATCH 2/4] Include packaging >= 25.0 dependency for GitHub wheel builds --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index ade4459..a6b1f3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ maintainers = [ dependencies = [ "numpy>=1.20.0", "matplotlib>=3.4.0", + "packaging >= 25.0", "pandas>=1.3.0", ] From cde1340f585b365b9f013053726bc6abab2b2df5 Mon Sep 17 00:00:00 2001 From: James Gilbert Date: Wed, 28 Jan 2026 12:58:17 +0000 Subject: [PATCH 3/4] Move packaging into build system requirements --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a6b1f3e..c5ab0f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,9 @@ [build-system] -requires = ["setuptools>=61.0", "wheel"] +requires = [ + "packaging >= 25.0", + "setuptools>=61.0", + "wheel", +] build-backend = "setuptools.build_meta" [project] @@ -28,7 +32,6 @@ maintainers = [ dependencies = [ "numpy>=1.20.0", "matplotlib>=3.4.0", - "packaging >= 25.0", "pandas>=1.3.0", ] From ca763e52ffc027a39d364006b6e5cf38f2f9a79b Mon Sep 17 00:00:00 2001 From: James Gilbert Date: Wed, 28 Jan 2026 13:21:16 +0000 Subject: [PATCH 4/4] Try latest version of cibuildwheel --- .github/workflows/build.yml | 4 ++-- pyproject.toml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 49e3b78..acf05fe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13","3.14"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 @@ -56,7 +56,7 @@ jobs: - uses: actions/checkout@v4 - name: Build wheels - uses: pypa/cibuildwheel@v2.21.3 + uses: pypa/cibuildwheel@v3.3.1 # Config is read from pyproject.toml [tool.cibuildwheel] - uses: actions/upload-artifact@v4 diff --git a/pyproject.toml b/pyproject.toml index c5ab0f5..766db9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [build-system] requires = [ - "packaging >= 25.0", "setuptools>=61.0", "wheel", ] @@ -72,7 +71,7 @@ smudgeplot = ["bin/*"] [tool.cibuildwheel] # Skip 32-bit, PyPy, musllinux, and Windows (no Windows support for now) skip = ["*-win32", "*-win_amd64", "*-manylinux_i686", "pp*", "*-musllinux*"] -build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*"] +build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"] environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" } # Test that the CLI works after building