Skip to content

Commit 6e42d1b

Browse files
Add progress/no-progress flag to CLI
Closes #240
1 parent b054efa commit 6e42d1b

File tree

3 files changed

+88
-33
lines changed

3 files changed

+88
-33
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.1.0 2024-06-10
2+
3+
- Add -Q/--no-progress flag to CLI
4+
15
# 0.0.10 2024-05-15
26
- Change output format of dexplode-init and dencode-init
37
- Bugfix for mac progress, and change of multiprocessing startup strategy.

bio2zarr/cli.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ def list_commands(self, ctx):
5858
help="Force overwriting of existing directories",
5959
)
6060

61+
progress = click.option(
62+
"-P /-Q",
63+
"--progress/--no-progress",
64+
default=True,
65+
help="Show progress bars (default: show)",
66+
)
67+
6168
one_based = click.option(
6269
"--one-based",
6370
is_flag=True,
@@ -190,9 +197,17 @@ def show_work_summary(work_summary, json):
190197
@verbose
191198
@column_chunk_size
192199
@compressor
200+
@progress
193201
@worker_processes
194202
def explode(
195-
vcfs, icf_path, force, verbose, column_chunk_size, compressor, worker_processes
203+
vcfs,
204+
icf_path,
205+
force,
206+
verbose,
207+
column_chunk_size,
208+
compressor,
209+
progress,
210+
worker_processes,
196211
):
197212
"""
198213
Convert VCF(s) to intermediate columnar format
@@ -205,7 +220,7 @@ def explode(
205220
worker_processes=worker_processes,
206221
column_chunk_size=column_chunk_size,
207222
compressor=get_compressor(compressor),
208-
show_progress=True,
223+
show_progress=progress,
209224
)
210225

211226

@@ -218,6 +233,7 @@ def explode(
218233
@compressor
219234
@json
220235
@verbose
236+
@progress
221237
@worker_processes
222238
def dexplode_init(
223239
vcfs,
@@ -228,6 +244,7 @@ def dexplode_init(
228244
compressor,
229245
json,
230246
verbose,
247+
progress,
231248
worker_processes,
232249
):
233250
"""
@@ -243,7 +260,7 @@ def dexplode_init(
243260
column_chunk_size=column_chunk_size,
244261
worker_processes=worker_processes,
245262
compressor=get_compressor(compressor),
246-
show_progress=True,
263+
show_progress=progress,
247264
)
248265
show_work_summary(work_summary, json)
249266

@@ -310,6 +327,7 @@ def mkschema(icf_path):
310327
@samples_chunk_size
311328
@max_variant_chunks
312329
@max_memory
330+
@progress
313331
@worker_processes
314332
def encode(
315333
icf_path,
@@ -321,6 +339,7 @@ def encode(
321339
samples_chunk_size,
322340
max_variant_chunks,
323341
max_memory,
342+
progress,
324343
worker_processes,
325344
):
326345
"""
@@ -337,7 +356,7 @@ def encode(
337356
max_variant_chunks=max_variant_chunks,
338357
worker_processes=worker_processes,
339358
max_memory=max_memory,
340-
show_progress=True,
359+
show_progress=progress,
341360
)
342361

343362

@@ -351,6 +370,7 @@ def encode(
351370
@samples_chunk_size
352371
@max_variant_chunks
353372
@json
373+
@progress
354374
@verbose
355375
def dencode_init(
356376
icf_path,
@@ -362,6 +382,7 @@ def dencode_init(
362382
samples_chunk_size,
363383
max_variant_chunks,
364384
json,
385+
progress,
365386
verbose,
366387
):
367388
"""
@@ -387,7 +408,7 @@ def dencode_init(
387408
variants_chunk_size=variants_chunk_size,
388409
samples_chunk_size=samples_chunk_size,
389410
max_variant_chunks=max_variant_chunks,
390-
show_progress=True,
411+
show_progress=progress,
391412
)
392413
show_work_summary(work_summary, json)
393414

@@ -413,12 +434,13 @@ def dencode_partition(zarr_path, partition, verbose, one_based):
413434
@click.command
414435
@zarr_path
415436
@verbose
416-
def dencode_finalise(zarr_path, verbose):
437+
@progress
438+
def dencode_finalise(zarr_path, verbose, progress):
417439
"""
418440
Final step for distributed conversion of ICF to VCF Zarr.
419441
"""
420442
setup_logging(verbose)
421-
vcf2zarr.encode_finalise(zarr_path, show_progress=True)
443+
vcf2zarr.encode_finalise(zarr_path, show_progress=progress)
422444

423445

424446
@click.command(name="convert")
@@ -428,6 +450,7 @@ def dencode_finalise(zarr_path, verbose):
428450
@variants_chunk_size
429451
@samples_chunk_size
430452
@verbose
453+
@progress
431454
@worker_processes
432455
def convert_vcf(
433456
vcfs,
@@ -436,6 +459,7 @@ def convert_vcf(
436459
variants_chunk_size,
437460
samples_chunk_size,
438461
verbose,
462+
progress,
439463
worker_processes,
440464
):
441465
"""
@@ -448,7 +472,7 @@ def convert_vcf(
448472
zarr_path,
449473
variants_chunk_size=variants_chunk_size,
450474
samples_chunk_size=samples_chunk_size,
451-
show_progress=True,
475+
show_progress=progress,
452476
worker_processes=worker_processes,
453477
)
454478

@@ -481,6 +505,7 @@ def vcf2zarr_main():
481505
@click.argument("in_path", type=click.Path())
482506
@click.argument("zarr_path", type=click.Path())
483507
@worker_processes
508+
@progress
484509
@verbose
485510
@variants_chunk_size
486511
@samples_chunk_size
@@ -489,6 +514,7 @@ def convert_plink(
489514
zarr_path,
490515
verbose,
491516
worker_processes,
517+
progress,
492518
variants_chunk_size,
493519
samples_chunk_size,
494520
):
@@ -499,7 +525,7 @@ def convert_plink(
499525
plink.convert(
500526
in_path,
501527
zarr_path,
502-
show_progress=True,
528+
show_progress=progress,
503529
worker_processes=worker_processes,
504530
samples_chunk_size=samples_chunk_size,
505531
variants_chunk_size=variants_chunk_size,

tests/test_cli.py

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,22 @@ def asjson(self):
6969
class TestWithMocks:
7070
vcf_path = "tests/data/vcf/sample.vcf.gz"
7171

72+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
7273
@mock.patch("bio2zarr.vcf2zarr.explode")
73-
def test_vcf_explode(self, mocked, tmp_path):
74+
def test_vcf_explode(self, mocked, tmp_path, progress, flag):
7475
icf_path = tmp_path / "icf"
7576
runner = ct.CliRunner(mix_stderr=False)
7677
result = runner.invoke(
7778
cli.vcf2zarr_main,
78-
f"explode {self.vcf_path} {icf_path}",
79+
f"explode {self.vcf_path} {icf_path} {flag}",
7980
catch_exceptions=False,
8081
)
8182
assert result.exit_code == 0
8283
assert len(result.stdout) == 0
8384
assert len(result.stderr) == 0
84-
mocked.assert_called_once_with(
85-
str(icf_path), (self.vcf_path,), **DEFAULT_EXPLODE_ARGS
86-
)
85+
args = dict(DEFAULT_EXPLODE_ARGS)
86+
args["show_progress"] = progress
87+
mocked.assert_called_once_with(str(icf_path), (self.vcf_path,), **args)
8788

8889
@pytest.mark.parametrize("compressor", ["lz4", "zstd"])
8990
@mock.patch("bio2zarr.vcf2zarr.explode")
@@ -286,23 +287,26 @@ def test_vcf_explode_missing_and_existing_vcf(self, mocked, tmp_path):
286287
assert "'no_such_file' does not exist" in result.stderr
287288
mocked.assert_not_called()
288289

290+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
289291
@mock.patch("bio2zarr.vcf2zarr.explode_init", return_value=FakeWorkSummary(5))
290-
def test_vcf_dexplode_init(self, mocked, tmp_path):
292+
def test_vcf_dexplode_init(self, mocked, tmp_path, progress, flag):
291293
runner = ct.CliRunner(mix_stderr=False)
292294
icf_path = tmp_path / "icf"
293295
result = runner.invoke(
294296
cli.vcf2zarr_main,
295-
f"dexplode-init {self.vcf_path} {icf_path} 5",
297+
f"dexplode-init {self.vcf_path} {icf_path} 5 {flag}",
296298
catch_exceptions=False,
297299
)
298300
assert result.exit_code == 0
299301
assert len(result.stderr) == 0
300302
assert list(result.stdout.split()) == ["num_partitions", "5"]
303+
args = dict(DEFAULT_DEXPLODE_INIT_ARGS)
304+
args["show_progress"] = progress
301305
mocked.assert_called_once_with(
302306
str(icf_path),
303307
(self.vcf_path,),
304308
target_num_partitions=5,
305-
**DEFAULT_DEXPLODE_INIT_ARGS,
309+
**args,
306310
)
307311

308312
@pytest.mark.parametrize("num_partitions", ["-- -1", "0", "asdf", "1.112"])
@@ -421,43 +425,51 @@ def test_mkschema(self, mocked, tmp_path):
421425
# mocked.assert_called_once_with("path", stdout)
422426
mocked.assert_called_once()
423427

428+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
424429
@mock.patch("bio2zarr.vcf2zarr.encode")
425-
def test_encode(self, mocked, tmp_path):
430+
def test_encode(self, mocked, tmp_path, progress, flag):
426431
icf_path = tmp_path / "icf"
427432
icf_path.mkdir()
428433
zarr_path = tmp_path / "zarr"
429434
runner = ct.CliRunner(mix_stderr=False)
430435
result = runner.invoke(
431-
cli.vcf2zarr_main, f"encode {icf_path} {zarr_path}", catch_exceptions=False
436+
cli.vcf2zarr_main,
437+
f"encode {icf_path} {zarr_path} {flag}",
438+
catch_exceptions=False,
432439
)
433440
assert result.exit_code == 0
434441
assert len(result.stdout) == 0
435442
assert len(result.stderr) == 0
443+
args = DEFAULT_ENCODE_ARGS
444+
args["show_progress"] = progress
436445
mocked.assert_called_once_with(
437446
str(icf_path),
438447
str(zarr_path),
439-
**DEFAULT_ENCODE_ARGS,
448+
**args,
440449
)
441450

451+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
442452
@mock.patch("bio2zarr.vcf2zarr.encode_init", return_value=FakeWorkSummary(10))
443-
def test_dencode_init(self, mocked, tmp_path):
453+
def test_dencode_init(self, mocked, tmp_path, progress, flag):
444454
icf_path = tmp_path / "icf"
445455
icf_path.mkdir()
446456
zarr_path = tmp_path / "zarr"
447457
runner = ct.CliRunner(mix_stderr=False)
448458
result = runner.invoke(
449459
cli.vcf2zarr_main,
450-
f"dencode-init {icf_path} {zarr_path} 10",
460+
f"dencode-init {icf_path} {zarr_path} 10 {flag}",
451461
catch_exceptions=False,
452462
)
453463
assert result.exit_code == 0
454464
assert list(result.stdout.split()) == ["num_partitions", "10"]
455465
assert len(result.stderr) == 0
466+
args = DEFAULT_DENCODE_INIT_ARGS
467+
args["show_progress"] = progress
456468
mocked.assert_called_once_with(
457469
str(icf_path),
458470
str(zarr_path),
459471
target_num_partitions=10,
460-
**DEFAULT_DENCODE_INIT_ARGS,
472+
**args,
461473
)
462474

463475
@mock.patch("bio2zarr.vcf2zarr.encode_partition")
@@ -494,32 +506,40 @@ def test_vcf_dencode_partition_one_based(self, mocked, tmp_path):
494506
str(zarr_path), 0, **DEFAULT_DENCODE_PARTITION_ARGS
495507
)
496508

509+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
497510
@mock.patch("bio2zarr.vcf2zarr.encode_finalise")
498-
def test_vcf_dencode_finalise(self, mocked, tmp_path):
511+
def test_vcf_dencode_finalise(self, mocked, tmp_path, progress, flag):
499512
runner = ct.CliRunner(mix_stderr=False)
500513
result = runner.invoke(
501-
cli.vcf2zarr_main, f"dencode-finalise {tmp_path}", catch_exceptions=False
514+
cli.vcf2zarr_main,
515+
f"dencode-finalise {tmp_path} {flag}",
516+
catch_exceptions=False,
502517
)
503518
assert result.exit_code == 0
504519
assert len(result.stdout) == 0
505520
assert len(result.stderr) == 0
506-
mocked.assert_called_once_with(str(tmp_path), **DEFAULT_DENCODE_FINALISE_ARGS)
521+
args = DEFAULT_DENCODE_FINALISE_ARGS
522+
args["show_progress"] = progress
523+
mocked.assert_called_once_with(str(tmp_path), **args)
507524

525+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
508526
@mock.patch("bio2zarr.vcf2zarr.convert")
509-
def test_convert_vcf(self, mocked):
527+
def test_convert_vcf(self, mocked, progress, flag):
510528
runner = ct.CliRunner(mix_stderr=False)
511529
result = runner.invoke(
512530
cli.vcf2zarr_main,
513-
f"convert {self.vcf_path} zarr_path",
531+
f"convert {self.vcf_path} zarr_path {flag}",
514532
catch_exceptions=False,
515533
)
516534
assert result.exit_code == 0
517535
assert len(result.stdout) == 0
518536
assert len(result.stderr) == 0
537+
args = dict(DEFAULT_CONVERT_ARGS)
538+
args["show_progress"] = progress
519539
mocked.assert_called_once_with(
520540
(self.vcf_path,),
521541
"zarr_path",
522-
**DEFAULT_CONVERT_ARGS,
542+
**args,
523543
)
524544

525545
@pytest.mark.parametrize("response", ["n", "N", "No"])
@@ -538,16 +558,19 @@ def test_vcf_convert_overwrite_zarr_confirm_no(self, mocked, tmp_path, response)
538558
assert "Aborted" in result.stderr
539559
mocked.assert_not_called()
540560

561+
@pytest.mark.parametrize(("progress", "flag"), [(True, "-P"), (False, "-Q")])
541562
@mock.patch("bio2zarr.plink.convert")
542-
def test_convert_plink(self, mocked):
563+
def test_convert_plink(self, mocked, progress, flag):
543564
runner = ct.CliRunner(mix_stderr=False)
544565
result = runner.invoke(
545-
cli.plink2zarr, ["convert", "in", "out"], catch_exceptions=False
566+
cli.plink2zarr, ["convert", "in", "out", flag], catch_exceptions=False
546567
)
547568
assert result.exit_code == 0
548569
assert len(result.stdout) == 0
549570
assert len(result.stderr) == 0
550-
mocked.assert_called_once_with("in", "out", **DEFAULT_CONVERT_ARGS)
571+
args = dict(DEFAULT_CONVERT_ARGS)
572+
args["show_progress"] = progress
573+
mocked.assert_called_once_with("in", "out", **args)
551574

552575
@pytest.mark.parametrize("response", ["y", "Y", "yes"])
553576
@mock.patch("bio2zarr.vcf2zarr.convert")
@@ -578,10 +601,11 @@ def test_dexplode(self, tmp_path, one_based):
578601
runner = ct.CliRunner(mix_stderr=False)
579602
result = runner.invoke(
580603
cli.vcf2zarr_main,
581-
f"dexplode-init {self.vcf_path} {icf_path} 5 --json",
604+
f"dexplode-init {self.vcf_path} {icf_path} 5 --json -Q",
582605
catch_exceptions=False,
583606
)
584607
assert result.exit_code == 0
608+
assert len(result.stderr) == 0
585609
assert json.loads(result.stdout)["num_partitions"] == 3
586610

587611
for j in range(3):
@@ -595,6 +619,7 @@ def test_dexplode(self, tmp_path, one_based):
595619
cli.vcf2zarr_main, f"dexplode-finalise {icf_path}", catch_exceptions=False
596620
)
597621
assert result.exit_code == 0
622+
assert len(result.stderr) == 0
598623

599624
result = runner.invoke(
600625
cli.vcf2zarr_main, f"inspect {icf_path}", catch_exceptions=False

0 commit comments

Comments
 (0)