Skip to content

Commit 26c346d

Browse files
chris-a-talbotjeromekelleher
authored andcommitted
Add stdin support to the tskit CLI
Update changelog to reflect stdin CLI feature
1 parent df2a253 commit 26c346d

3 files changed

Lines changed: 38 additions & 3 deletions

File tree

python/CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
[1.0.4] - 2026-XX-XX
33
--------------------
44

5+
**Features**
6+
7+
- CLI commands that load a tree sequence now accept ``-`` as the input path to
8+
read from stdin. (:issue:`3468`)
9+
510
--------------------
611
[1.0.3] - 2026-05-14
712
--------------------

python/tests/test_cli.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ def capture_output(func, *args, **kwargs):
7373
return stdout_output, stderr_output
7474

7575

76+
class MockStdIn:
77+
def __init__(self, buffer):
78+
self.buffer = buffer
79+
80+
7681
class TestCli(unittest.TestCase):
7782
"""
7883
Superclass of tests for the CLI needing temp files.
@@ -310,6 +315,16 @@ def test_vcf_allow_position_zero(self, flags, expected):
310315
assert args.tree_sequence == tree_sequence
311316
assert args.allow_position_zero == expected
312317

318+
def test_vcf_stdin_file(self):
319+
parser = cli.get_tskit_parser()
320+
args = parser.parse_args(["vcf", "-"])
321+
assert args.tree_sequence == "-"
322+
323+
def test_vcf_requires_tree_sequence(self):
324+
parser = cli.get_tskit_parser()
325+
with pytest.raises(SystemExit):
326+
parser.parse_args(["vcf"])
327+
313328
def test_info_default_values(self):
314329
parser = cli.get_tskit_parser()
315330
cmd = "info"
@@ -560,6 +575,13 @@ def test_vcf(self):
560575
assert len(stderr) == 0
561576
self.verify_vcf(stdout)
562577

578+
def test_vcf_stdin(self):
579+
with open(self._tree_sequence_file, "rb") as f:
580+
with mock.patch("sys.stdin", MockStdIn(f)):
581+
stdout, stderr = capture_output(cli.tskit_main, ["vcf", "-0", "-"])
582+
assert len(stderr) == 0
583+
self.verify_vcf(stdout)
584+
563585
def verify_info(self, ts, output_info):
564586
assert str(ts) == output_info
565587

python/tskit/cli.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,15 @@ def sys_exit(message):
4545

4646

4747
def load_tree_sequence(path):
48+
if path in [None, "-"]:
49+
path = getattr(sys.stdin, "buffer", sys.stdin)
4850
try:
4951
return tskit.load(path)
50-
except OSError as e:
51-
sys_exit(f"Load error: {e}")
52+
except (OSError, EOFError, tskit.FileFormatError) as e:
53+
message = str(e)
54+
if isinstance(e, EOFError) and len(message) == 0:
55+
message = "End of file"
56+
sys_exit(f"Load error: {message}")
5257

5358

5459
def run_info(args):
@@ -134,7 +139,10 @@ def run_vcf(args):
134139

135140

136141
def add_tree_sequence_argument(parser):
137-
parser.add_argument("tree_sequence", help="The tskit tree sequence file")
142+
parser.add_argument(
143+
"tree_sequence",
144+
help="The tskit tree sequence file, or '-' for stdin",
145+
)
138146

139147

140148
def add_precision_argument(parser):

0 commit comments

Comments
 (0)