Skip to content

Commit 9740ec4

Browse files
Merge pull request #28 from jeromekelleher/bgen-unphased
Bgen unphased
2 parents 3e93cfa + c21547a commit 9740ec4

5 files changed

Lines changed: 51 additions & 4 deletions

File tree

biofuse/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ def mount_bgen(vcz_url, mount_dir, basename, access_log_path, **kwargs):
111111
FUSE process's memory; only ``.bgen`` reads cross the wire.
112112
``--no-sample-file`` and ``--no-bgi`` suppress the corresponding
113113
sidecar; ``--no-header-samples`` drops the sample identifiers from
114-
the ``.bgen`` header block.
114+
the ``.bgen`` header block. ``--unphased`` ignores the input's
115+
``call_genotype_phased`` field and encodes every variant with the
116+
BGEN phased flag clear.
115117
116118
The bcftools-view-style filter / backend / log options are inherited
117119
from ``vcztools view-bgen``; see ``vcztools view-bgen --help`` for the

biofuse/formats.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,11 @@ def _plink_encoder_factory(reader, opts):
129129

130130

131131
def _bgen_encoder_factory(reader, opts):
132-
return vcztools.BgenEncoder(reader, embed_header_samples=not opts.no_header_samples)
132+
return vcztools.BgenEncoder(
133+
reader,
134+
embed_header_samples=not opts.no_header_samples,
135+
unphased=opts.unphased,
136+
)
133137

134138

135139
PLINK_SPEC = FormatSpec(

tests/test_bgen_apps.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,19 @@ async def test_no_header_samples_stable_across_opens(
182182
data = await trio.to_thread.run_sync(bgen_path.read_bytes)
183183
assert data == expected, f"cycle {cycle} differed from reference"
184184

185+
@pytest.mark.parametrize("unphased", [True, False])
186+
async def test_unphased_stable_across_opens(self, tmp_path, fx_small_vcz, unphased):
187+
opts = dataclasses.replace(vcztools.ViewBgenOptions(), unphased=unphased)
188+
ref_reader = opts.make_reader(str(fx_small_vcz.path))
189+
with vcztools.BgenEncoder(ref_reader, unphased=unphased) as ref:
190+
expected = ref.read(0, ref.total_size)
191+
192+
async with _mount_bgen(tmp_path, fx_small_vcz, opts=opts) as (mnt, basename):
193+
bgen_path = mnt / f"{basename}.bgen"
194+
for cycle in range(3):
195+
data = await trio.to_thread.run_sync(bgen_path.read_bytes)
196+
assert data == expected, f"cycle {cycle} differed from reference"
197+
185198

186199
def _pread_sync(path: pathlib.Path, off: int, size: int) -> bytes:
187200
with path.open("rb") as f:

tests/test_formats.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,34 @@ def test_embed_default_matches_in_process(self, fx_reader, fx_small_vcz):
313313
assert data == ref_data
314314

315315

316+
class TestBgenUnphasedToggle:
317+
"""``--unphased`` flows through to ``BgenEncoder(unphased=True)``.
318+
319+
With the flag set, the encoder must ignore ``call_genotype_phased``
320+
and produce the same bytes as an in-process ``BgenEncoder`` built
321+
with ``unphased=True`` on the same reader.
322+
"""
323+
324+
def test_unphased_matches_in_process(self, fx_reader, fx_small_vcz):
325+
opts = vcztools.ViewBgenOptions(unphased=True)
326+
with formats.BGEN_SPEC.encoder_factory(fx_reader, opts) as encoder:
327+
data = encoder.read(0, encoder.total_size)
328+
ref_reader = _open_reader(fx_small_vcz.path)
329+
with vcztools.BgenEncoder(ref_reader, unphased=True) as ref:
330+
ref_data = ref.read(0, ref.total_size)
331+
assert data == ref_data
332+
333+
def test_unphased_default_matches_in_process(self, fx_reader, fx_small_vcz):
334+
opts = vcztools.ViewBgenOptions()
335+
assert opts.unphased is False
336+
with formats.BGEN_SPEC.encoder_factory(fx_reader, opts) as encoder:
337+
data = encoder.read(0, encoder.total_size)
338+
ref_reader = _open_reader(fx_small_vcz.path)
339+
with vcztools.BgenEncoder(ref_reader, unphased=False) as ref:
340+
ref_data = ref.read(0, ref.total_size)
341+
assert data == ref_data
342+
343+
316344
class TestSpecsRegistry:
317345
def test_specs_dict_has_both_entries(self):
318346
assert formats.SPECS == {"plink": formats.PLINK_SPEC, "bgen": formats.BGEN_SPEC}

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)