Skip to content

Commit 765a90c

Browse files
authored
Merge pull request #248 from jgrg/master
Check subprocess exit and tidy command line generation
2 parents 9049082 + ca763e5 commit 765a90c

5 files changed

Lines changed: 53 additions & 52 deletions

File tree

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
fail-fast: false
1515
matrix:
1616
os: [ubuntu-latest, macos-latest]
17-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13","3.14"]
17+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
1818

1919
steps:
2020
- uses: actions/checkout@v4
@@ -56,7 +56,7 @@ jobs:
5656
- uses: actions/checkout@v4
5757

5858
- name: Build wheels
59-
uses: pypa/cibuildwheel@v2.21.3
59+
uses: pypa/cibuildwheel@v3.3.1
6060
# Config is read from pyproject.toml [tool.cibuildwheel]
6161

6262
- uses: actions/upload-artifact@v4

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[build-system]
2-
requires = ["setuptools>=61.0", "wheel"]
2+
requires = [
3+
"setuptools>=61.0",
4+
"wheel",
5+
]
36
build-backend = "setuptools.build_meta"
47

58
[project]
@@ -68,7 +71,7 @@ smudgeplot = ["bin/*"]
6871
[tool.cibuildwheel]
6972
# Skip 32-bit, PyPy, musllinux, and Windows (no Windows support for now)
7073
skip = ["*-win32", "*-win_amd64", "*-manylinux_i686", "pp*", "*-musllinux*"]
71-
build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*"]
74+
build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"]
7275
environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" }
7376

7477
# Test that the CLI works after building

src/lib/PloidyList.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1222,7 +1222,7 @@ int main(int argc, char *argv[])
12221222
int flags[128];
12231223
char *eptr;
12241224

1225-
ARG_INIT("PloidyList");
1225+
ARG_INIT("extract_kmer_pairs");
12261226

12271227
OUT = NULL;
12281228
ETHRESH = 4;

src/lib/PloidyPlot.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,7 @@ int main(int argc, char *argv[])
12471247
int flags[128];
12481248
char *eptr;
12491249

1250-
ARG_INIT("PloidyPlot");
1250+
ARG_INIT("hetmers");
12511251

12521252
OUT = NULL;
12531253
ETHRESH = 4;

src/smudgeplot/cli.py

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
import argparse
44
import os
5+
import shlex
56
import shutil
7+
import subprocess
68
import sys
79
from importlib.metadata import version
810
from pathlib import Path
11+
from typing import Any
912

1013
import numpy as np
1114

@@ -41,30 +44,32 @@ def get_binary_path(name: str) -> str:
4144
if system_binary:
4245
return system_binary
4346

44-
raise FileNotFoundError(
45-
f"Binary '{name}' not found. Please ensure smudgeplot is properly installed. "
47+
msg = (
48+
f"Binary '{name}' not found. Please ensure smudgeplot is properly installed.\n"
4649
f"Checked locations:\n"
47-
f" - Package: {bundled_binary}\n"
48-
f" - System PATH: (not found)\n"
50+
f" - Package: {bundled_binary.parent}\n"
51+
f" - System PATH: {os.get_exec_path()}\n"
4952
f"\nYou may need to reinstall smudgeplot or install the binaries manually."
5053
)
54+
raise FileNotFoundError(msg)
5155

5256

53-
def run_binary(name: str, args: str) -> int:
57+
def run_binary(name: str, args: list[Any]) -> None:
5458
"""
5559
Run a binary with the given arguments.
5660
5761
Args:
5862
name: Name of the binary
59-
args: Space-separated argument string
63+
args: List of (stringify-able) arguments
6064
61-
Returns:
62-
Return code from the binary
65+
Throws:
66+
subprocess.CalledProcessError on non-zero exit of the command
6367
"""
64-
binary_path = get_binary_path(name)
65-
cmd = f"{binary_path} {args}"
66-
sys.stderr.write(f"Calling: {name} {args}\n")
67-
return os.system(cmd)
68+
cmd_line = [get_binary_path(name)]
69+
for x in args:
70+
cmd_line.append(str(x))
71+
sys.stderr.write(f"Calling: {shlex.join(cmd_line)}\n")
72+
subprocess.run(cmd_line, check=True)
6873

6974

7075
class Parser:
@@ -332,41 +337,38 @@ def main():
332337
fin()
333338

334339
if _parser.task == "hetmers":
335-
# PloidyPlot is expected to be installed in the system as well as the R library supporting it
336-
plot_args = " -o" + str(args.o)
337-
plot_args += " -e" + str(args.L)
338-
plot_args += " -T" + str(args.t)
340+
hetmer_args = [
341+
f"-o{args.o}",
342+
f"-e{args.L}",
343+
f"-T{args.t}",
344+
]
339345
if args.verbose:
340-
plot_args += " -v"
346+
hetmer_args.append("-v")
341347
if args.tmp != ".":
342-
plot_args += " -P" + args.tmp
343-
plot_args += " " + args.infile
348+
hetmer_args.append(f"-P{args.tmp}")
349+
hetmer_args.append(args.infile)
344350

345-
run_binary("hetmers", plot_args)
351+
run_binary("hetmers", hetmer_args)
346352

347353
fin()
348354

349355
if _parser.task == "extract":
350-
plot_args = " -o" + str(args.o)
351-
plot_args += " -T" + str(args.t)
356+
extract_args = [
357+
f"-o{args.o}",
358+
f"-T{args.t}",
359+
]
352360
if args.verbose:
353-
plot_args += " -v"
361+
extract_args.append("-v")
354362
if args.tmp != ".":
355-
plot_args += " -P" + args.tmp
356-
plot_args += " " + args.infile
357-
if args.sma.endswith(".sma"):
358-
plot_args += " " + args.sma.removesuffix(".sma")
359-
else:
360-
plot_args += " " + args.sma
363+
extract_args.append(f"-P{args.tmp}")
364+
extract_args.append(args.infile)
365+
extract_args.append(args.sma.removesuffix(".sma"))
361366

362-
run_binary("extract_kmer_pairs", plot_args)
367+
run_binary("extract_kmer_pairs", extract_args)
363368

364369
fin()
365370

366-
if args.title:
367-
title=args.title
368-
else:
369-
title = ".".join(args.infile.split("/")[-1].split(".")[0:2])
371+
title = args.title or str(Path(args.infile).with_suffix("").name)
370372

371373
if _parser.task == "plot":
372374
smudge_tab = smg.read_csv(args.smudgefile, sep="\t", names=["structure", "size", "rel_size"])
@@ -396,12 +398,11 @@ def main():
396398
coverages.local_aggregation(distance=args.d, noise_filter=1000, mask_errors=True)
397399
coverages.count_kmers()
398400
sys.stderr.write(
399-
f"\t\
400-
Total kmers: {coverages.total_kmers}\n\t \
401-
Genomic kmers: {coverages.total_genomic_kmers}\n\t \
402-
Genomic kmers in smudges: {coverages.total_genomic_kmers_in_smudges}\n\t \
403-
Sequencing errors: {coverages.total_error_kmers}\n\t \
404-
Fraction of errors: {round(coverages.total_error_kmers/coverages.total_kmers, 3)}"
401+
f"\nTotal kmers: {coverages.total_kmers}\n"
402+
f"Genomic kmers: {coverages.total_genomic_kmers}\n"
403+
f"Genomic kmers in smudges: {coverages.total_genomic_kmers_in_smudges}\n"
404+
f"Sequencing errors: {coverages.total_error_kmers}\n"
405+
f"Fraction of errors: {coverages.error_fraction:.3f}\n"
405406
)
406407

407408
smudge_size_cutoff = (
@@ -420,11 +421,7 @@ def main():
420421
delimiter="\t",
421422
)
422423

423-
limit = 0.7
424-
if coverages.error_fraction < limit:
425-
cov = smudges.cov
426-
else:
427-
cov = 0
424+
cov = smudges.cov if coverages.error_fraction < 0.7 else 0
428425

429426
sys.stderr.write("\nCreating centrality plot\n")
430427
smudges.centrality_plot(args.o, args.format)
@@ -459,10 +456,11 @@ def main():
459456
json_report=args.json_report,
460457
input_params=vars(args),
461458
palette=args.col_ramp,
462-
invert_cols=args.invert_cols
459+
invert_cols=args.invert_cols,
463460
)
464461

465462
fin()
466463

464+
467465
if __name__ == "__main__":
468466
main()

0 commit comments

Comments
 (0)