Skip to content

feat(vcf)!: scan VCF files with keyword-only options instead of kwargs - #17

Open
Hoeze wants to merge 4 commits into
feat/drop-legacy-helpersfrom
feat/vcf-scan-api
Open

Hoeze wants to merge 4 commits into
feat/drop-legacy-helpersfrom
feat/vcf-scan-api

Conversation

@Hoeze

@Hoeze Hoeze commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

tl;dr: scan_vcf_variants and scan_vcf_genotypes take typed keyword-only options instead of passing kwargs to polars-bio, and Variant becomes a frozen, validated value of chrom, pos, ref and alt. Both scans give REF and ALT in uppercase.

scan_vcf_variants passed all keyword arguments to polars_bio.scan_vcf. Its samples and format_fields returned the raw genotypes struct, which scan_vcf_genotypes already unpacks, and use_zero_based did not change the output.

scan_vcf_variants(path, *, info_fields=(), sequence_alts_only=True) now takes only these two options. With the default sequence_alts_only=True, it keeps only the ALT alleles that match (?i)^[ACGTN]+$, the bases of the VCF specification. With sequence_alts_only=False, it also keeps the ALT alleles that are not sequences of bases, e.g. *, <DEL> and breakends, and a missing ALT (.) gives a null alt. allele_idx stays the index in the ALT field of the record. The pattern is the one constant _SEQUENCE_ALT. scan_vcf_genotypes(path, *, samples=None, format_fields=("GT",), info_fields=(), sequence_alts_only=True, carriers_only=True) takes the same two options, so it can also give the carriers of symbolic ALT alleles. Both take a path-like object as path. They raise a TypeError if info_fields, format_fields or samples is a str or not a sequence of strings.

Both scans give ref, and the ALT alleles that are sequences of bases, in uppercase. VCF bases are case-insensitive, but Encoder and Tokenizer are case-sensitive, so they now take the scanned sequences as they are. With sequence_alts_only=False, the other ALT alleles keep their text, e.g. the chromosome name of a breakend. Variant keeps the strings that it gets.

Variant(chrom, pos, ref, alt) is a frozen dataclass, and its constructor checks the four fields once. It raises a TypeError for arguments of the wrong type instead of converting them, so pos=10.7 no longer gives pos=10. It raises a ValueError for a pos below 1 or an empty ref. It takes numpy integers as pos. A Variant is equal only to a Variant with the same fields. from_str, __str__, start and end stay.

id, qual, filter, info, source and copy() of Variant are gone. They were the record fields of cyvcf2: kipoiseq2 never set them, and VariantSeqExtractor reads only chrom, pos, ref and alt. The VCF columns ID, QUAL, FILTER and INFO stay in the frames of scan_vcf_variants, e.g. scan_vcf_variants(path).select("chrom", "pos", "id", "qual", "filter"). A frozen Variant needs no copy().

The README shows sequence_alts_only and says to use False only for tables, because VariantSeqExtractor inserts symbolic ALT alleles as text. It also says that ref and alt are uppercase. MIGRATING.md lists the breaking changes, and its kipoiseq section no longer says that Variant.source stays.

BREAKING CHANGE: scan_vcf_variants takes only info_fields and sequence_alts_only as keyword arguments, so samples, format_fields, use_zero_based and the other options of polars_bio.scan_vcf are gone. Use scan_vcf_genotypes for the FORMAT fields. scan_vcf_genotypes takes its options only as keyword arguments. Both raise a TypeError if info_fields, format_fields or samples is a str or not a sequence of strings. Both give ref and alt in uppercase, while 0.1 kept the case of the file. Variant takes only chrom, pos, ref and alt: its id, qual, filter, info, source and copy() are gone, so select these VCF columns from scan_vcf_variants instead. Variant is frozen. It raises a TypeError instead of converting wrong types, and a ValueError for pos < 1 or an empty ref. A Variant is no longer equal to other objects with the same chrom, pos, ref and alt.

feat(dataclasses)!: validate the fields of Variant
feat(dataclasses)!: freeze Variant and keep only chrom, pos, ref and alt
feat(vcf)!: uppercase REF and ALT in the VCF scans

@Hoeze
Hoeze added this pull request to stack #16 October 16, 2026 23:52
@Hoeze
Hoeze force-pushed the feat/vcf-scan-api branch from 8223a34 to 085ab2e Compare October 17, 2026 14:08
@Hoeze
Hoeze force-pushed the feat/vcf-scan-api branch from 085ab2e to 1dd5842 Compare October 17, 2026 16:22
Hoeze added 4 commits October 17, 2026 18:36
Variant raises a TypeError for arguments of the wrong type instead of
converting them, so pos=10.7 no longer gives pos 10. It raises a
ValueError for a pos below 1 or an empty REF. qual defaults to None,
as polars-bio gives a missing QUAL. source was a cyvcf2 leftover that
kipoiseq2 never set.

BREAKING CHANGE: Variant takes id, qual, filter and info only as keyword
arguments, and source is gone. It raises a TypeError instead of
converting wrong types, and a ValueError for pos < 1 or an empty ref.
Variant.qual defaults to None instead of 0.
scan_vcf_variants passed all keyword arguments to polars_bio.scan_vcf.
Its samples and format_fields returned the raw genotypes struct, which
scan_vcf_genotypes already unpacks, and use_zero_based did not change
the output. It dropped only the ALT alleles that contain N or *, so
VariantSeqExtractor applied a missing ALT (.) as a deletion, and it
inserted symbolic ALT alleles such as <DEL> and breakends as text.

scan_vcf_variants now takes info_fields and sequence_alts_only. With
sequence_alts_only=True, it keeps only the ALT alleles that are
sequences of the bases A, C, G, T and N, in any case. With False, it
keeps all ALT alleles, and gives a missing ALT (.) as a null alt.
allele_idx counts the dropped ALT alleles too. scan_vcf_genotypes takes
the same two options and its other options as keyword arguments. Both
take a path-like object as path, and raise a TypeError for a str as
info_fields, format_fields or samples.

BREAKING CHANGE: scan_vcf_variants takes only info_fields and
sequence_alts_only as keyword arguments, so samples, format_fields,
use_zero_based and the other options of polars_bio.scan_vcf are gone.
scan_vcf_genotypes takes its options only as keyword arguments. Both
raise a TypeError if info_fields, format_fields or samples is a str.
By default, both drop the ALT alleles that are not sequences of bases,
and keep those that contain N.
Variant held the cyvcf2 record fields id, qual, filter and info next to
chrom, pos, ref and alt. kipoiseq2 never set info, and
VariantSeqExtractor reads only chrom, pos, ref and alt. The VCF columns
ID, QUAL, FILTER and INFO stay in the frames of scan_vcf_variants.

Variant is now a frozen dataclass, and its constructor checks the four
fields once. copy() is gone, because a frozen Variant needs no copy. A
Variant is equal only to a Variant with the same fields.

BREAKING CHANGE: Variant takes only chrom, pos, ref and alt. Its id,
qual, filter, info and copy() are gone: select the VCF columns from
scan_vcf_variants instead. A Variant is no longer equal to other objects
with the same chrom, pos, ref and alt.
scan_vcf_variants and scan_vcf_genotypes give ref, and the ALT alleles
that are sequences of bases, in uppercase. VCF bases are
case-insensitive, but Encoder and Tokenizer are case-sensitive, so they
now take the scanned sequences as they are.
With sequence_alts_only=False, the other ALT alleles keep their text,
e.g. the chromosome name of a breakend. Variant keeps the strings that
it gets.

BREAKING CHANGE: scan_vcf_variants and scan_vcf_genotypes give ref and
alt in uppercase. MultiSampleVCF kept the case of the file.
@Hoeze
Hoeze force-pushed the feat/vcf-scan-api branch from 1dd5842 to 9ed35c4 Compare October 17, 2026 17:19

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant