File readers and I/O#

Single-pass readers for the common genomic formats, random-access FASTA, the format detector, and the record/flag types they yield. See the User Guide for the iteration contract and grove-loading workflows.

Readers#

class pygenogrove.BedReader#

Bases: pybind11_object

A single-pass iterator over the records of a BED file.

Iterate it directly to get BedEntry objects:

for entry in pygenogrove.BedReader("peaks.bed"):
    ...

Plain and BGZF/gzip-compressed (.gz) files are both accepted (the format is auto-detected). The reader owns an htslib file handle and is single-pass — it cannot be restarted or iterated twice.

Parameters:
  • path (str) – Path to the BED file. A missing/unreadable file raises an exception.

  • skip_invalid_lines (bool, optional) – Controls handling of malformed records after the first. If False (default) such a line raises RuntimeError mid-iteration; if True it is skipped silently. NOTE: the first data record is validated when the reader is constructed, so a malformed first record raises immediately at construction regardless of this flag.

  • region (str, optional) – A tabix region string (“chr”, “chr:start-end”) in tabix query coordinates (1-based, inclusive) — note this differs from BED’s own 0-based half-open convention. When set, only records overlapping the region are yielded; requires a bgzip-compressed, tabix-indexed file. Empty (default) streams the whole file.

get_current_line(self: pygenogrove.BedReader) int#

1-based physical line number consumed so far (comments/blanks count); 0 before the first read.

get_error_message(self: pygenogrove.BedReader) str#

Error message from the most recent read; empty on clean EOF.

class pygenogrove.GffReader#

Bases: pybind11_object

A single-pass iterator over the records of a GFF3/GTF file.

Iterate it directly to get GffEntry objects:

for entry in pygenogrove.GffReader("genes.gff3"):
    ...

The GFF3 vs GTF format is auto-detected per record (see GffEntry.format). Plain and BGZF/gzip-compressed (.gz) files are both accepted. The reader owns an htslib file handle and is single-pass.

Parameters:
  • path (str) – Path to the GFF/GTF file. A missing/unreadable file raises an exception.

  • skip_invalid_lines (bool, optional) – Controls handling of malformed records after the first. If False (default) such a line raises RuntimeError mid-iteration; if True it is skipped silently. NOTE: the first data record is validated when the reader is constructed, so a malformed first record raises immediately at construction regardless of this flag.

  • validate_gtf (bool, optional) – If True, validate the mandatory GTF2 attributes (gene_id, transcript_id).

  • region (str, optional) – A tabix region string (“chr”, “chr:start-end”) in tabix query coordinates (1-based, inclusive). When set, only records overlapping the region are yielded; requires a bgzip-compressed, tabix-indexed file. Empty (default) streams the whole file.

get_current_line(self: pygenogrove.GffReader) int#

1-based physical line number consumed so far (comments/blanks count); 0 before the first read.

get_error_message(self: pygenogrove.GffReader) str#

Error message from the most recent read; empty on clean EOF.

class pygenogrove.BamReader#

Bases: pybind11_object

A single-pass iterator over the alignments of a SAM/BAM file.

Iterate it directly to get SamEntry objects:

for aln in pygenogrove.BamReader("reads.bam"):
    ...

SAM/BAM are auto-detected by htslib. The reader owns an htslib handle and is single-pass — it cannot be restarted or iterated twice.

Parameters:
  • path (str) – Path to the SAM/BAM file. A missing/unreadable file raises.

  • skip_unmapped (bool, optional) – Skip unmapped reads (default True).

  • skip_secondary (bool, optional) – Skip the corresponding alignment categories (default False).

  • skip_supplementary (bool, optional) – Skip the corresponding alignment categories (default False).

  • skip_qc_fail (bool, optional) – Skip the corresponding alignment categories (default False).

  • skip_duplicates (bool, optional) – Skip the corresponding alignment categories (default False).

  • min_mapq (int, optional) – Minimum mapping quality; reads below it are skipped (0 = no filter).

get_current_line(self: pygenogrove.BamReader) int#

Records consumed so far (advances on skipped/filtered records too).

get_error_message(self: pygenogrove.BamReader) str#

Error message from the most recent read; empty on clean EOF.

class pygenogrove.VcfReader#

Bases: pybind11_object

Single-pass iterator over a VCF/BCF file (plain, bgzip-ed, or binary BCF; htslib auto-detects). Yields VcfEntry. Not thread-safe — drive one reader per thread.

for v in pygenogrove.VcfReader(“calls.vcf”, skip_filtered=True):

get_contigs(self: pygenogrove.VcfReader) list[str]#

Contig names declared in the header.

get_current_line(self: pygenogrove.VcfReader) int#

1-based index of the most recently consumed record (counts records dropped by skip_filtered too); 0 before the first read.

get_error_message(self: pygenogrove.VcfReader) str#

Error message from the most recent read; empty on clean EOF.

get_header(self: pygenogrove.VcfReader) str#

Full VCF header text (## meta lines + the #CHROM column line).

get_sample_names(self: pygenogrove.VcfReader) list[str]#

Sample names in column order (empty for sites-only VCFs).

class pygenogrove.FastaReader#

Bases: pybind11_object

A single-pass iterator over the records of a FASTA or FASTQ file.

Iterate it directly to get FastaEntry objects:

for rec in pygenogrove.FastaReader("genome.fa"):
    print(rec.name, len(rec))

FASTA (>) and FASTQ (@) are auto-detected, and plain or gzip/BGZF-compressed (.gz) inputs are accepted. The reader owns an htslib handle and is single-pass — it cannot be restarted or iterated twice.

Parameters:
  • path (str) – Path to the FASTA/FASTQ file. A missing/unreadable file raises.

  • skip_empty_sequences (bool, optional) – Skip records whose sequence is empty (default False).

get_current_line(self: pygenogrove.FastaReader) int#

1-based physical line number consumed so far; 0 before the first read.

get_error_message(self: pygenogrove.FastaReader) str#

Error message from the most recent read; empty on clean EOF.

class pygenogrove.FastaIndex#

Bases: pybind11_object

Random-access reader for a FASTA file, backed by an .fai index.

Opening builds the index if it is missing (this writes a sibling .fai file, so the FASTA’s directory must be writable on first open). Region coordinates are 0-based half-open [start, end) — to fetch the bases of a GenomicCoordinate (0-based closed [start, end]), call idx.fetch(index, gc.start, gc.end + 1).

Non-copyable; the underlying htslib handle is closed when the object is garbage-collected.

fetch(*args, **kwargs)#

Overloaded function.

  1. fetch(self: pygenogrove.FastaIndex, name: str, start: int, end: int) -> str

    fetch(name, start, end) -> str

    Bases of sequence name over the 0-based half-open region [start, end). Raises IndexError if name is unknown or the region is invalid (start >= end, or beyond htslib’s limit).

  2. fetch(self: pygenogrove.FastaIndex, name: str) -> str

    fetch(name) -> str

    The entire sequence named name. Raises IndexError if unknown.

has_sequence(self: pygenogrove.FastaIndex, name: str) bool#

Whether name is present in the index.

names(self: pygenogrove.FastaIndex) list[str]#

List of all sequence names, in index order.

sequence_count(self: pygenogrove.FastaIndex) int#

Number of sequences in the index.

sequence_length(self: pygenogrove.FastaIndex, name: str) int#

Length in bases of sequence name. Raises IndexError if unknown.

sequence_name(self: pygenogrove.FastaIndex, index: int) str#

Name of the i-th sequence (0-based). Raises IndexError if out of range.

Record types#

class pygenogrove.SamEntry#

Bases: pybind11_object

A single SAM/BAM alignment record (htslib-native coordinates: start is 0-based, end is 0-based exclusive).

Load alignments into the universal Grove as JSON, deriving the strand-aware key from the record:

g = pygenogrove.Grove()
for aln in pygenogrove.BamReader("reads.bam"):
    g.insert(aln.chrom, aln.to_coordinate(), aln.to_dict())
property chrom#

Reference name (RNAME)

property cigar#

CIGAR string form, e.g. ‘100M’ (‘*’ if none).

consumes_reference(self: pygenogrove.SamEntry) bool#

Covers >= 1 reference base (False for unmapped / pure soft-clip).

property end#

0-based exclusive end (POS + CIGAR ref length)

property flags#

The AlignmentFlags (FLAG)

get_strand(self: pygenogrove.SamEntry) str#

Strand char from the FLAG: ‘+’ forward, ‘-’ reverse, ‘.’ unmapped.

is_mapped(self: pygenogrove.SamEntry) bool#

The read is mapped.

is_primary(self: pygenogrove.SamEntry) bool#

Not secondary and not supplementary.

property mapq#

Mapping quality (0-255)

property qname#

Query/read name (QNAME)

property quality#

ASCII quality scores (QUAL)

property sequence#

Read sequence (SEQ)

property start#

0-based start (htslib-native, from POS)

to_coordinate(self: pygenogrove.SamEntry) pygenogrove.GenomicCoordinate#

Derive the strand-aware GenomicCoordinate key for this alignment (strand from the FLAG; 0-based half-open [start, end) -> closed [start, end-1]). Raises ValueError if the read covers no reference bases (unmapped or a zero-ref-consuming CIGAR).

to_dict(self: pygenogrove.SamEntry) dict#

A dict of the core alignment fields (qname, mapq, strand, cigar, flags, is_primary, is_mapped) — convenient as a Grove JSON payload.

class pygenogrove.FastaEntry#

Bases: pybind11_object

A single FASTA/FASTQ record: a named nucleotide sequence.

For FASTQ records, quality holds the per-base quality string; for FASTA it is None. name is the header text up to the first whitespace, and comment is the rest of the header line.

property comment#

Optional description (rest of the header line).

is_fastq(self: pygenogrove.FastaEntry) bool#

Whether this record carries quality scores (i.e. came from FASTQ).

property name#

Sequence name (header text up to the first whitespace).

property quality#

Per-base quality string (FASTQ only; None for FASTA).

property sequence#

Nucleotide sequence.

class pygenogrove.VcfEntry#

Bases: pybind11_object

A single VCF/BCF variant record (0-based half-open start/end, decoupled from any grove key type).

Load variants into the universal Grove as JSON, deriving the key from the record:

g = pygenogrove.Grove()
for v in pygenogrove.VcfReader("calls.vcf"):
    g.insert(v.chrom, v.to_coordinate(), v.to_dict())
property alt#

ALT alleles (empty for monomorphic records).

property chrom#

CHROM (contig name).

property end#

0-based exclusive end (start + len(REF)).

property filter#

FILTER entries; [‘PASS’] when passed, empty when ‘.’.

property format#

FORMAT keys in column order (incl. ‘GT’); empty unless parse_samples.

property id#

ID, empty when ‘.’.

property info#

name -> bool / list[int] / list[float] / str.

Type:

INFO fields (when parse_info)

is_indel(self: pygenogrove.VcfEntry) bool#

True if any sequence ALT differs in length from REF.

is_snp(self: pygenogrove.VcfEntry) bool#

True for a simple biallelic single-base SNP.

passed_filter(self: pygenogrove.VcfEntry) bool#

True if FILTER is PASS or unset (‘.’).

property qual#

QUAL score (only valid when qual_missing is False).

property qual_missing#

True when QUAL is ‘.’ (missing).

property ref#

REF allele.

property samples#

Per-sample SampleGenotype list, parallel to VcfReader.get_sample_names().

property start#

0-based start (htslib position; VCF POS - 1).

to_coordinate(self: pygenogrove.VcfEntry) pygenogrove.GenomicCoordinate#

Derive the GenomicCoordinate key for this variant (unstranded ‘.’; 0-based half-open [start, end) -> closed [start, end-1]). Raises ValueError if the record spans no reference bases.

to_dict(self: pygenogrove.VcfEntry) dict#

A JSON-serializable dict of the core variant fields (id, ref, alt, qual, filter, is_snp, is_indel) — convenient as a Grove payload.

class pygenogrove.SampleGenotype#

Bases: pybind11_object

Genotype and FORMAT data for one sample at one variant record.

property fields#

Other FORMAT fields keyed by tag (e.g. ‘DP’, ‘GQ’), excluding GT. Values are list[int] / list[float] / str.

property gt_alleles#

Decoded GT allele indices (0 = REF, 1.. = ALT index, -1 = missing ‘.’); empty when no GT field.

gt_string(self: pygenogrove.SampleGenotype) str#

GT as a VCF-style string (‘0/1’, ‘0|1’, ‘./.’, ‘’ if no GT).

property has_gt#

True if a GT field was present for this sample.

is_hom_ref(self: pygenogrove.SampleGenotype) bool#

True if every called allele is the reference (all 0); a missing genotype is not hom-ref.

property phased#

Whether the genotype is phased (the ‘|’ separator).

Note

VcfEntry.is_symbolic_allele(allele) (static) — returns True when allele is a symbolic / non-reference placeholder such as <*>, <NON_REF>, or *. It is documented here manually and excluded from autodoc above because its binding docstring contains an unescaped * that the RST parser misreads; re-include it once that is fixed upstream in pygenogrove.

Alignment flags#

class pygenogrove.SamFlags#

Bases: pybind11_object

SAM/BAM FLAG bit constants (for has_flag()).

DUPLICATE = 1024#
MATE_REVERSE = 32#
MATE_UNMAPPED = 8#
PAIRED = 1#
PROPER_PAIR = 2#
QCFAIL = 512#
READ1 = 64#
READ2 = 128#
REVERSE = 16#
SECONDARY = 256#
SUPPLEMENTARY = 2048#
UNMAPPED = 4#
class pygenogrove.AlignmentFlags#

Bases: pybind11_object

The SAM/BAM bitwise FLAG, with convenience accessors.

has_flag(self: pygenogrove.AlignmentFlags, flag: int) bool#

Whether a specific FLAG bit (see SamFlags) is set.

is_duplicate(self: pygenogrove.AlignmentFlags) bool#
is_mate_reverse(self: pygenogrove.AlignmentFlags) bool#
is_mate_unmapped(self: pygenogrove.AlignmentFlags) bool#
is_paired(self: pygenogrove.AlignmentFlags) bool#
is_proper_pair(self: pygenogrove.AlignmentFlags) bool#
is_qc_fail(self: pygenogrove.AlignmentFlags) bool#
is_read1(self: pygenogrove.AlignmentFlags) bool#
is_read2(self: pygenogrove.AlignmentFlags) bool#
is_reverse(self: pygenogrove.AlignmentFlags) bool#
is_secondary(self: pygenogrove.AlignmentFlags) bool#
is_supplementary(self: pygenogrove.AlignmentFlags) bool#
is_unmapped(self: pygenogrove.AlignmentFlags) bool#
value(self: pygenogrove.AlignmentFlags) int#

Raw 16-bit FLAG value.

Format detection#

class pygenogrove.FiletypeDetector#

Bases: pybind11_object

Detects a file’s format and compression from its extension + magic bytes.

detect_filetype(self: pygenogrove.FiletypeDetector, path: str) tuple[pygenogrove.Filetype, pygenogrove.CompressionType]#

detect_filetype(path) -> (Filetype, CompressionType)

Infer the file format and compression of path. Returns a tuple, e.g. (Filetype.BED, CompressionType.GZIP) for “peaks.bed.gz”. Unrecognized inputs yield Filetype.UNKNOWN / CompressionType.UNKNOWN.

class pygenogrove.Filetype#

Bases: pybind11_object

Detected file format.

Members:

BED

BEDGRAPH

GFF

GTF

VCF

SAM

BAM

FASTA

FASTQ

GG

UNKNOWN

BAM = <Filetype.BAM: 6>#
BED = <Filetype.BED: 0>#
BEDGRAPH = <Filetype.BEDGRAPH: 1>#
FASTA = <Filetype.FASTA: 7>#
FASTQ = <Filetype.FASTQ: 8>#
GFF = <Filetype.GFF: 2>#
GG = <Filetype.GG: 9>#
GTF = <Filetype.GTF: 3>#
SAM = <Filetype.SAM: 5>#
UNKNOWN = <Filetype.UNKNOWN: 10>#
VCF = <Filetype.VCF: 4>#
property name#
property value#
class pygenogrove.CompressionType#

Bases: pybind11_object

Detected compression.

Members:

NONE

GZIP

BZIP2

XZ

ZSTD

LZ4

UNKNOWN

BZIP2 = <CompressionType.BZIP2: 2>#
GZIP = <CompressionType.GZIP: 1>#
LZ4 = <CompressionType.LZ4: 5>#
NONE = <CompressionType.NONE: 0>#
UNKNOWN = <CompressionType.UNKNOWN: 6>#
XZ = <CompressionType.XZ: 3>#
ZSTD = <CompressionType.ZSTD: 4>#
property name#
property value#