Skip to content

Commit 3e93cfa

Browse files
Merge pull request #26 from jeromekelleher/yet-more-cli
Yet more cli
2 parents 8e5e7b4 + bbb8397 commit 3e93cfa

5 files changed

Lines changed: 54 additions & 41 deletions

File tree

biofuse/cli.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def biofuse_main():
6363
"""biofuse: read-only FUSE filesystem views over VCF Zarr data."""
6464

6565

66-
@biofuse_main.command(name="mount-plink")
66+
@biofuse_main.command(name="mount-plink", cls=vcztools.GroupedCommand)
6767
@click.argument("vcz_url", type=str)
6868
@click.argument(
6969
"mount_dir",
@@ -88,13 +88,11 @@ def mount_plink(vcz_url, mount_dir, basename, access_log_path, **kwargs):
8888
8989
The mount runs in the foreground until interrupted with Ctrl-C.
9090
"""
91-
opts = vcztools.ViewPlinkOptions.pop_from_click_kwargs(kwargs)
92-
_run_mount(
93-
formats.PLINK_SPEC, vcz_url, mount_dir, basename, access_log_path, opts, kwargs
94-
)
91+
opts = vcztools.ViewPlinkOptions.from_click_kwargs(kwargs)
92+
_run_mount(formats.PLINK_SPEC, vcz_url, mount_dir, basename, access_log_path, opts)
9593

9694

97-
@biofuse_main.command(name="mount-bgen")
95+
@biofuse_main.command(name="mount-bgen", cls=vcztools.GroupedCommand)
9896
@click.argument("vcz_url", type=str)
9997
@click.argument(
10098
"mount_dir",
@@ -119,15 +117,12 @@ def mount_bgen(vcz_url, mount_dir, basename, access_log_path, **kwargs):
119117
from ``vcztools view-bgen``; see ``vcztools view-bgen --help`` for the
120118
full reference. The encoder always uses zlib level 0 (stored,
121119
fixed-size blocks) so byte-range random access into the mounted
122-
``.bgen`` is O(1); ``--compression-level`` is accepted for parity
123-
with ``view-bgen`` but has no effect.
120+
``.bgen`` is O(1).
124121
125122
The mount runs in the foreground until interrupted with Ctrl-C.
126123
"""
127-
opts = vcztools.ViewBgenOptions.pop_from_click_kwargs(kwargs)
128-
_run_mount(
129-
formats.BGEN_SPEC, vcz_url, mount_dir, basename, access_log_path, opts, kwargs
130-
)
124+
opts = vcztools.ViewBgenOptions.from_click_kwargs(kwargs)
125+
_run_mount(formats.BGEN_SPEC, vcz_url, mount_dir, basename, access_log_path, opts)
131126

132127

133128
def _run_mount(
@@ -137,9 +132,7 @@ def _run_mount(
137132
basename: str | None,
138133
access_log_path: str | None,
139134
opts,
140-
extra_kwargs: dict,
141135
) -> None:
142-
assert extra_kwargs == {}, extra_kwargs
143136
opts.log.apply()
144137

145138
mount_dir_path = pathlib.Path(mount_dir)

biofuse/formats.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,18 @@
2727
sample identifiers from its header block.
2828
2929
For BGEN the ``.bgen.bgi`` sidecar is a SQLite database that
30-
:func:`vcztools.bgen.write_bgen_index` writes to a filesystem path.
31-
We materialise it to a tempfile at session-init time, read the bytes
32-
back, and hold them in the server process's memory alongside the
33-
``.sample`` text.
30+
:func:`vcztools.write_bgi` writes to a filesystem path. We materialise
31+
it to a tempfile at session-init time, read the bytes back, and hold
32+
them in the server process's memory alongside the ``.sample`` text.
3433
"""
3534

3635
import dataclasses
36+
import io
3737
import pathlib
3838
import tempfile
3939
from collections.abc import Callable
4040

4141
import vcztools
42-
from vcztools import bgen as vcztools_bgen
43-
from vcztools import plink as vcztools_plink
4442

4543

4644
@dataclasses.dataclass(frozen=True)
@@ -86,9 +84,13 @@ def _plink_static_suffixes(opts) -> tuple[str, ...]:
8684
def _build_plink_static(reader, opts) -> dict[str, bytes]:
8785
out: dict[str, bytes] = {}
8886
if not opts.no_bim:
89-
out[".bim"] = vcztools_plink.generate_bim(reader).encode("utf-8")
87+
buf = io.StringIO()
88+
vcztools.write_bim(reader, buf)
89+
out[".bim"] = buf.getvalue().encode("utf-8")
9090
if not opts.no_fam:
91-
out[".fam"] = vcztools_plink.generate_fam(reader).encode("utf-8")
91+
buf = io.StringIO()
92+
vcztools.write_fam(reader, buf)
93+
out[".fam"] = buf.getvalue().encode("utf-8")
9294
return out
9395

9496

@@ -104,18 +106,20 @@ def _bgen_static_suffixes(opts) -> tuple[str, ...]:
104106
def _build_bgen_static(reader, opts) -> dict[str, bytes]:
105107
out: dict[str, bytes] = {}
106108
if not opts.no_sample_file:
107-
out[".sample"] = vcztools_bgen.generate_sample(reader).encode("utf-8")
109+
buf = io.StringIO()
110+
vcztools.write_sample(reader, buf)
111+
out[".sample"] = buf.getvalue().encode("utf-8")
108112
if not opts.no_bgi:
109-
# ``write_bgen_index`` takes a filesystem path. Materialise the
110-
# .bgi into a TemporaryDirectory, read the bytes back, then let
111-
# the context manager clean up. The encoder used to harvest
112-
# ``variant_offsets`` is I/O-free in ``__init__`` and is
113-
# discarded once the offsets are read.
113+
# ``write_bgi`` requires a filesystem path (sqlite3.connect needs
114+
# a real path). Materialise the .bgi into a TemporaryDirectory,
115+
# read the bytes back, then let the context manager clean up.
116+
# The encoder used to harvest ``variant_offsets`` is I/O-free in
117+
# ``__init__`` and is discarded once the offsets are read.
114118
with vcztools.BgenEncoder(reader) as encoder:
115119
variant_offsets = encoder.variant_offsets
116120
with tempfile.TemporaryDirectory(prefix="biofuse-bgen-") as tmp_dir:
117121
bgi_path = pathlib.Path(tmp_dir) / "index.bgen.bgi"
118-
vcztools_bgen.write_bgen_index(reader, str(bgi_path), variant_offsets)
122+
vcztools.write_bgi(reader, str(bgi_path), variant_offsets)
119123
out[".bgen.bgi"] = bgi_path.read_bytes()
120124
return out
121125

tests/test_cli.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ def test_mount_plink_help(self):
3535
"--log-level",
3636
]:
3737
assert flag in result.output, f"missing {flag} in mount-plink help"
38+
for section in [
39+
"Selection options:",
40+
"Zarr store options:",
41+
"Reader options:",
42+
"Logging options:",
43+
]:
44+
assert section in result.output, f"missing {section} in mount-plink help"
3845

3946
def test_mount_bgen_help(self):
4047
result = CliRunner().invoke(cli.biofuse_main, ["mount-bgen", "--help"])
@@ -47,10 +54,16 @@ def test_mount_bgen_help(self):
4754
"--no-sample-file",
4855
"--no-bgi",
4956
"--no-header-samples",
50-
"--compression-level",
5157
"--log-level",
5258
]:
5359
assert flag in result.output, f"missing {flag} in mount-bgen help"
60+
for section in [
61+
"Selection options:",
62+
"Zarr store options:",
63+
"Reader options:",
64+
"Logging options:",
65+
]:
66+
assert section in result.output, f"missing {section} in mount-bgen help"
5467

5568
def test_version(self):
5669
result = CliRunner().invoke(cli.biofuse_main, ["--version"])

tests/test_formats.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@
77
the same selection.
88
"""
99

10+
import io
1011
import sqlite3
1112
import tempfile
1213

1314
import pytest
1415
import vcztools
15-
from vcztools import bgen as vcztools_bgen
16-
from vcztools import plink as vcztools_plink
1716

1817
from biofuse import formats
1918

2019

20+
def _write_to_string(writer, reader):
21+
buf = io.StringIO()
22+
writer(reader, buf)
23+
return buf.getvalue().encode("utf-8")
24+
25+
2126
def _open_reader(path):
2227
return vcztools.ViewPlinkOptions().make_reader(str(path))
2328

@@ -54,11 +59,11 @@ def test_identifiers(self, fx_plink_opts):
5459
assert formats.PLINK_SPEC.streaming_kind == "bed"
5560
assert formats.PLINK_SPEC.static_suffixes(fx_plink_opts) == (".bim", ".fam")
5661

57-
def test_static_files_match_generators(self, fx_reader, fx_plink_opts):
62+
def test_static_files_match_writers(self, fx_reader, fx_plink_opts):
5863
static = formats.PLINK_SPEC.build_static_files(fx_reader, fx_plink_opts)
5964
assert set(static) == {".bim", ".fam"}
60-
assert static[".bim"] == vcztools_plink.generate_bim(fx_reader).encode("utf-8")
61-
assert static[".fam"] == vcztools_plink.generate_fam(fx_reader).encode("utf-8")
65+
assert static[".bim"] == _write_to_string(vcztools.write_bim, fx_reader)
66+
assert static[".fam"] == _write_to_string(vcztools.write_fam, fx_reader)
6267

6368
def test_encoder_total_size(self, fx_reader, fx_small_vcz, fx_plink_opts):
6469
with formats.PLINK_SPEC.encoder_factory(fx_reader, fx_plink_opts) as encoder:
@@ -81,11 +86,9 @@ def test_static_files_keys_match_suffixes(self, fx_reader, fx_bgen_opts):
8186
static = formats.BGEN_SPEC.build_static_files(fx_reader, fx_bgen_opts)
8287
assert set(static) == {".sample", ".bgen.bgi"}
8388

84-
def test_sample_matches_generator(self, fx_reader, fx_bgen_opts):
89+
def test_sample_matches_writer(self, fx_reader, fx_bgen_opts):
8590
static = formats.BGEN_SPEC.build_static_files(fx_reader, fx_bgen_opts)
86-
assert static[".sample"] == vcztools_bgen.generate_sample(fx_reader).encode(
87-
"utf-8"
88-
)
91+
assert static[".sample"] == _write_to_string(vcztools.write_sample, fx_reader)
8992

9093
def test_bgi_opens_as_sqlite(self, fx_reader, tmp_path, fx_small_vcz, fx_bgen_opts):
9194
static = formats.BGEN_SPEC.build_static_files(fx_reader, fx_bgen_opts)

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)