Python bindings for the genogrove C++ library - a specialized B+ tree for genomic intervals
Project description
pygenogrove
Python bindings for the genogrove C++ library - a specialized B+ tree data structure optimized for genomic interval storage and querying.
Installation
Building from Source
Requirements:
- C++20 compatible compiler
- CMake 3.15+
- Python 3.8+
# Clone with submodules
git clone --recursive https://github.com/genogrove/pygenogrove.git
cd pygenogrove
# Build using CMake
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
# The built module will be in build/pygenogrove.so (or .pyd on Windows)
Using pip
pip install pygenogrove
Using conda/mamba
Quick Start
The standard key is a GenomicCoordinate (stranded, 0-based closed
[start, end]), and the standard Grove stores any JSON-serializable
payload (dict / list / scalar / None) per key:
import pygenogrove as pg
grove = pg.Grove()
# Insert stranded coordinates with arbitrary metadata (or no data at all)
grove.insert("chr1", pg.GenomicCoordinate("+", 100, 200), {"gene": "FOO", "score": 5})
grove.insert("chr1", pg.GenomicCoordinate("-", 100, 200), {"gene": "BAR"})
grove.insert("chr1", pg.GenomicCoordinate(".", 300, 400)) # data defaults to None
# Query is strand-aware: a '+' query matches only '+' (and '*' wildcards)
for key in grove.intersect(pg.GenomicCoordinate("+", 150, 160), "chr1"):
print(key.value, key.data) # GenomicCoordinate('+', 100, 200) {'gene': 'FOO', 'score': 5}
# '*' matches any strand; '.' is a concrete unstranded value (matches only '.')
len(grove.intersect(pg.GenomicCoordinate("*", 150, 160), "chr1")) # 2
grove.serialize("out.gg") # JSON-text payload; a C++ grove<gc, string> can read it
The payload round-trips transparently (no json import needed), and each key
may carry a different shape — no schema is enforced.
Important — do not mutate an inserted coordinate. GenomicCoordinate.start,
.end, and .strand are read-only; set_range() / set_strand() must only be
used on coordinates you have NOT yet inserted (e.g. a query you want to reuse).
Mutating a stored key silently corrupts B+ tree ordering.
Strand semantics
'+'/'-'— forward / reverse strand'.'— a concrete unstranded value (matches only'.')'*'— wildcard query strand (matches any strand)
So plain unstranded intervals are just GenomicCoordinate('.', start, end).
Typed BED/GFF groves (for C++ interop)
The schemaless Grove is the everyday tool. When you need a guaranteed BED/GFF
structure and full interop with typed C++ .gg files, use the typed groves
(BedGrove / GffGrove, also genomic-coordinate keyed):
g = pg.BedGrove()
g.insert("chr1", bed_entry) # entry-deriving: strand taken from the BED6 column
API Reference
GenomicCoordinate
GenomicCoordinate(strand: str, start: int, end: int)
A stranded genomic coordinate with closed [start, end] (0-based, both
inclusive). strand is one of '+', '-', '.', '*'. Overlap requires both
coordinate overlap AND strand compatibility ('*' matches any).
Attributes (read-only): strand, start, end
Methods:
set_range(start, end)/set_strand(strand): pre-insertion only (mutating a stored key corrupts B+ tree ordering).GenomicCoordinate.overlaps(a, b): static strand-aware overlap check.
Grove
Grove(order: int = 3)
A B+ tree container for genomic intervals with multi-index support.
Parameters:
order: Maximum branching factor (max keys per node = order - 1). Minimum 3.
Methods:
len(grove)/size()/indexed_vertex_count(): Number of indexed intervals across all indicesget_order(): Get the order (branching factor) of the treeinsert(index: str, key: GenomicCoordinate, data=None) -> Key: Insert a coordinate (with an optional JSON-serializable payload) at the specified indexintersect(query: GenomicCoordinate) -> QueryResult: Find strand-aware overlaps across all indicesintersect(query: GenomicCoordinate, index: str) -> QueryResult: Find strand-aware overlaps in a specific indexflanking(query: GenomicCoordinate, index: str) -> FlankingResult: Find the nearest non-overlapping keys on either side of the query (predecessor / successor). Alsoflanking(query, index, is_compatible)filters candidates by abool(candidate, query)predicate (e.g. same strand)
FlankingResult (returned by flanking):
predecessor: the closest key entirely before the query (aKey), orNonesuccessor: the closest key entirely after the query (aKey), orNone
Keys overlapping the query are excluded; for nested intervals the predecessor is
the one with the largest end (smallest gap). Compute the gap distance from the
returned key, e.g. query.start - result.predecessor.value.end - 1 (closed
coordinates). BedGrove/GffGrove expose flanking too (their results' keys
carry .data).
Graph overlay (directed edges between keys):
add_edge(source: Key, target: Key): Add a directed edge (raisesValueErrorif a key isNone)remove_edge(source: Key, target: Key) -> bool: Remove an edge;Trueif one was removedhas_edge(source: Key, target: Key) -> bool: Test whether an edge existsget_neighbors(source: Key) -> list[Key]: Keys directly reachable fromsourceout_degree(source: Key) -> int: Number of outgoing edges fromsourceedge_count() -> int: Total number of edges in the overlayvertex_count_with_edges() -> int: Number of keys with at least one outgoing edgeadd_external_key(key: GenomicCoordinate, data=None) -> Key: Add a key outside the index that can still participate in the graph (not returned byintersect)
Labelled edges — on the universal Grove, edges carry a JSON-serializable payload (the typed BedGrove/GffGrove keep unlabelled edges for binary interop, so these methods are absent there):
add_edge(source: Key, target: Key, data): Add an edge with a metadata payload. The 2-argumentadd_edgeattachesNoneget_edges(source: Key) -> list: The edge payloads ofsource's outgoing edges, parallel toget_neighbors(source)get_edge_list(source: Key) -> list[tuple[Key, object]]: The outgoing edges as(target, metadata)pairs — the zip ofget_neighborsandget_edges(metadata isNonefor payload-less edges)get_neighbors_if(source: Key, predicate) -> list[Key]: Target keys whose edge metadata satisfiespredicate(metadata)— the predicate receives the decoded payload (edges added without a payload yieldNone, so guard for it when mixing labelled and unlabelled edges)link_with(keys: list[Key], predicate): Label adjacent pairs —predicate(k1, k2)returns the edge payload to attach, orNoneto skip
Edge removal / bulk linking (on every grove):
remove_edges_from(source: Key) -> int/remove_edges_to(target: Key) -> int/remove_all_edges(key: Key) -> int: Remove outgoing / incoming / all touching edges; each returns the count removedremove_edges_if(predicate) -> int: Remove every edge matching a predicate. On the universalGrovethe predicate ispredicate(target: Key, metadata) -> bool(sees both target and edge metadata); on void-edgeBedGrove/GffGroveit ispredicate(target: Key) -> bool. Returns the count removedclear_graph(): Remove all edges (keys are left intact);graph_empty() -> boollink_if(keys: list[Key], predicate): Add an unlabelled edge between each adjacent pair(keys[i], keys[i+1])for whichpredicate(k1, k2)returnsTrue(typically over the keys returned by a bulk insert)
import pygenogrove as pg
g = pg.Grove()
a = g.insert("chr1", pg.GenomicCoordinate("+", 100, 200))
b = g.insert("chr1", pg.GenomicCoordinate("+", 300, 400))
g.add_edge(a, b, {"type": "exon->transcript", "weight": 7})
g.get_edges(a) # [{"type": ..., "weight": 7}]
g.get_neighbors_if(a, lambda m: m["weight"] > 5) # [b]
Serialization (zlib-compressed .gg binary):
serialize(path: str): Write the grove (coordinates + payloads + graph overlay) topathdeserialize(path: str) -> Grove(static): Load a grove written byserialize
Removal / storage:
remove_key(index: str, key: Key) -> bool: Remove a key (and its graph edges);Trueif found.None/unknown index →Falsecompact(): Reclaim dead slots left byremove_key(). ⚠️ Invalidates every previously-returned indexedKey— re-discover via a fresh query afterwardvertex_count()/external_vertex_count()/key_storage_size(): counts (indexed + external; external-only; total storage slots incl. dead)
Key
Wrapper object for a coordinate stored in the grove. Returned by insert operations
and yielded by query results. Keeps its Grove alive.
Attributes:
value: theGenomicCoordinate(returned by copy — mutating it cannot corrupt ordering)data: the payload. On the universalGrovethis is the JSON value you stored (dict / list / scalar /None), returned as a freshly decoded copy each access. On the typedBedKey/GffKeyit is a live, mutable reference to the record.
QueryResult
Result object containing matching intervals from a query.
Attributes:
query: The query interval used for the searchkeys: List of matching keys
Methods:
__len__(): Number of results__iter__(): Iterate over matching keys
Point key types — Numeric & Kmer
Two non-interval key types whose overlap is exact equality (not range
intersection), so their groves act as point dictionaries. Each has its own
*Grove / *Key / *QueryResult with the same surface as Grove — optional
JSON payload, labelled edges, serialize / deserialize.
import pygenogrove as pg
# Numeric — integer point keys (ids, timestamps, …)
g = pg.NumericGrove()
g.insert("ids", pg.Numeric(42), {"label": "answer"})
list(g.intersect(pg.Numeric(42), "ids"))[0].data # {'label': 'answer'}
len(g.intersect(pg.Numeric(43), "ids")) # 0 — exact match only
# Kmer — 2-bit-encoded DNA k-mers (k ≤ 32, A/C/G/T case-insensitive)
km = pg.KmerGrove()
km.insert("seqs", pg.Kmer("ACGT"), {"count": 3})
str(pg.Kmer("acgt")) # 'ACGT' (normalized)
pg.Kmer.is_valid("ACGN") # False
Numeric: value (read-only; set_value pre-insertion only), overlaps(a, b),
comparisons, str/repr. Kmer: Kmer(sequence) or Kmer(encoding, k),
encoding / k / len(), overlaps(a, b), static is_valid(sequence) and
max_k (32). Invalid bases or k > 32 raise ValueError.
BedGrove (typed BED grove)
BedGrove (grove<genomic_coordinate, bed_entry>) is the typed alternative
to the schemaless Grove: instead of a JSON payload, each key carries a
structured BedEntry. Use it when you want a guaranteed BED schema and full
interop with typed C++ .gg files (prebuilt BED groves load/save with their
records intact, and the GTF-style helpers are available on GffGrove).
import pygenogrove as pg
g = pg.BedGrove(100)
# insert(index, coord, data) — the GenomicCoordinate is the key, BedEntry is the payload
entry = pg.BedEntry("chr1", 1000, 2000) # BED-native coords (0-based, half-open)
entry.name = "BRCA1"
entry.score = 900
entry.strand = "+"
key = g.insert("chr1", pg.GenomicCoordinate(".", 1000, 1999), entry)
# the returned key exposes both the interval value and the BED payload
print(key.value.start, key.data.name) # 1000 BRCA1
for hit in g.intersect(pg.GenomicCoordinate(".", 1500, 1600), "chr1"):
print(hit.data.name, hit.data.score)
# serialize/deserialize preserves the BedEntry data
g.serialize("genes.gg")
reloaded = pg.BedGrove.deserialize("genes.gg")
BedGrove exposes the same surface as Grove (multi-index insert/intersect,
the graph overlay, and serialize/deserialize), with these differences:
insert(index: str, key: GenomicCoordinate, data: BedEntry) -> BedKeytakes the BED payload.add_external_key(key: GenomicCoordinate, data: BedEntry) -> BedKeytakes the payload too.- Entry-deriving inserts (no hand-conversion of coordinates):
insert(index, entry) -> BedKey— a 2-argument overload: pass a bareBedEntryand the GenomicCoordinate key is derived from its native coordinates (BED's half-open[s, e)→ closed[s, e-1]; GFF's 1-based[s, e]→[s-1, e-1]). This is the foolproof way to load records from a reader.insert_bulk(index, entries, presorted=False) -> list[BedKey]— same idea for a whole list of bare entries.
- Fast-path inserts (data-carrying groves only):
insert_sorted(index, interval, data) -> BedKey— single insert on the rightmost-append path (skips tree traversal).insert_bulk(index, items, presorted=False) -> list[BedKey]— insert many explicit(GenomicCoordinate, BedEntry)records at once (10–20× faster for large datasets; an empty index is built bottom-up in O(n)).presorted=Trueassumes the records are already sorted by interval (skips the internal sort).- Precondition: sorted/bulk inserts require ascending intervals, and when
appending to a non-empty index every new interval must be greater than all
existing ones. Violating this corrupts B+ tree ordering — use plain
insertif unsure. (GffGrovehas all the same methods.)
BedKey is like Key but adds a data attribute:
value: the interval (returned by copy; do not rely on mutating it)data: the associatedBedEntry— a live, mutable reference (unlikevalue, the payload is not part of the B+ tree ordering, so editing it in place is safe)
BedQueryResult is the BedGrove analog of QueryResult (its keys are BedKeys).
BedEntry
A single BED record. Coordinates are BED-native: 0-based, half-open [start, end)
(distinct from the closed [start, end] of GenomicCoordinate used as the grove key).
BedEntry(chrom: str, start: int, end: int)
Attributes (read/write):
chrom(str),start(int),end(int)name:Optional[str](BED4+)score:Optional[int](BED5+)strand:Optional[str]— a single character ('+','-','.'); assigning an empty or multi-character string raisesValueError,Noneclears it (BED6+)thickness:Optional[ThickInfo](BED7+)item_rgb:Optional[RgbColor](BED9+)blocks:Optional[BlockInfo](BED12)
ThickInfo(start, end), RgbColor(red, green, blue) (channels 0–255), and
BlockInfo(count, sizes, starts) (with list[int] sizes/starts) are the
supporting value types. List fields are returned/assigned by copy.
GffGrove (typed GFF/GTF grove)
GffGrove (grove<genomic_coordinate, gff_entry>) is the same typed grove for
GFF3/GTF records — identical surface to BedGrove, with a GffEntry payload
instead of BedEntry:
import pygenogrove as pg
g = pg.GffGrove(100)
entry = pg.GffEntry("chr1", 1000, 2000, "gene") # GFF-native coords (1-based, inclusive)
entry.source = "ensembl"
entry.strand = "+"
entry.attributes = {"gene_id": "ENSG1", "gene_name": "BRCA1"}
key = g.insert("chr1", pg.GenomicCoordinate(".", 999, 1999), entry)
print(key.data.type, key.data.get_gene_id()) # gene ENSG1
for hit in g.intersect(pg.GenomicCoordinate(".", 1500, 1600), "chr1"):
print(hit.data.type, dict(hit.data.attributes))
g.serialize("genes.gg")
reloaded = pg.GffGrove.deserialize("genes.gg")
GffKey mirrors BedKey (value is a copy, data is a live mutable GffEntry
reference); GffQueryResult is the GffGrove analog of QueryResult.
GffEntry
A single GFF3/GTF record. Coordinates are GFF-native: 1-based, both endpoints
inclusive (distinct from GenomicCoordinate's 0-based closed and BedEntry's 0-based
half-open).
GffEntry(seqid: str, start: int, end: int, type: str)
Attributes (read/write):
seqid(str),source(str),type(str),start(int),end(int)score:Optional[float]strand:Optional[str]— a single character ('+','-','.','?'); empty or multi-character raisesValueError,Noneclears itphase:Optional[int](CDS phase 0/1/2)attributes:dict[str, str]— the column-9 key/value pairs (returned/assigned by copy)format: aGffFormatenum (GFF3/GTF/UNKNOWN)
Methods: is_gtf(), is_gff3(), get_attribute(key), and the GTF helpers
get_gene_id(), get_transcript_id(), get_exon_number(), get_gene_name(),
get_gene_biotype() (each returns None when the attribute is absent).
BedReader / GffReader (file iterators)
BedReader and GffReader are single-pass iterators over BED and GFF3/GTF
files. Iterate them to get BedEntry / GffEntry records. Plain and
gzip/BGZF-compressed (.gz) files are both accepted (auto-detected).
import pygenogrove as pg
# read records one at a time
for entry in pg.BedReader("peaks.bed"):
print(entry.chrom, entry.start, entry.end, entry.name)
# the common workflow: load a file into a grove. The 2-argument insert derives
# the grove's 0-based closed GenomicCoordinate key from each entry's native coordinates,
# so you don't hand-convert (BED half-open, GFF 1-based) yourself.
g = pg.BedGrove(256)
for e in pg.BedReader("peaks.bed"):
g.insert(e.chrom, e)
gff = pg.GffGrove(256)
for e in pg.GffReader("genes.gff3"):
gff.insert(e.seqid, e)
# bulk-load one chromosome at a time (insert_bulk is per-index):
g2 = pg.BedGrove(256)
g2.insert_bulk("chr1", [e for e in pg.BedReader("peaks.bed") if e.chrom == "chr1"])
BedReader(path: str, skip_invalid_lines: bool = False)
GffReader(path: str, skip_invalid_lines: bool = False, validate_gtf: bool = False)
- A missing/unreadable
pathraises on construction. - With
skip_invalid_lines=False(default) a malformed line raisesRuntimeErrormid-iteration; withTruesuch lines are skipped. The first data record is validated when the reader is constructed, so a malformed first record raises immediately regardless of this flag. GffReader(..., validate_gtf=True)enforces the mandatory GTF2 attributes (gene_id,transcript_id).- Both expose
get_error_message()andget_current_line()for diagnostics. - The readers are single-pass — they own an htslib file handle and cannot be restarted or iterated twice.
Coordinate systems —
GenomicCoordinateis 0-based closed[start, end];BedEntryis 0-based half-open[start, end);GffEntryis 1-based inclusive[start, end]. Convert deliberately when building grove keys, as shown above.
BamReader (SAM/BAM alignments)
BamReader is a single-pass iterator over SAM/BAM files (htslib auto-detects the
format) yielding SamEntry records, with filtering options applied during
iteration.
import pygenogrove as pg
for aln in pg.BamReader("reads.bam", min_mapq=30):
print(aln.qname, aln.chrom, aln.start, aln.end, aln.get_strand())
# load alignments into the universal Grove (sam_entry isn't serializable, so
# there's no typed BamGrove — route through to_coordinate() + to_dict())
g = pg.Grove(256)
for aln in pg.BamReader("reads.bam"):
if aln.is_mapped():
g.insert(aln.chrom, aln.to_coordinate(), aln.to_dict())
BamReader(path, skip_unmapped=True, skip_secondary=False,
skip_supplementary=False, skip_qc_fail=False,
skip_duplicates=False, min_mapq=0)
SamEntryfields:qname,chrom,start,end(0-based half-open),mapq,sequence,quality,cigar(string form),flags(anAlignmentFlags). Helpers:get_strand(),is_primary()/is_mapped()/is_reverse()/is_secondary()/is_supplementary()/is_duplicate()/is_paired()/ … ,consumes_reference(),has_flag(flag),to_coordinate()(strand-aware key) andto_dict()(JSON payload).SamFlagsexposes the standard FLAG bit constants;AlignmentFlags(the.flagsobject) hasvalue()plus the sameis_*()predicates.- CIGAR element detail, mate info, and aux tags are not yet exposed.
FastaReader (FASTA/FASTQ sequences)
FastaReader is a single-pass iterator over FASTA/FASTQ files (auto-detected;
.gz accepted) yielding FastaEntry records. Sequences are named records, not
intervals, so this reader is standalone (no grove integration).
import pygenogrove as pg
for rec in pg.FastaReader("genome.fa"):
print(rec.name, rec.comment, len(rec), rec.is_fastq())
FastaReader(path, skip_empty_sequences=False)
FastaEntryfields:name,comment,sequence,quality(Optional[str], FASTQ only);is_fastq(),len(entry).
FastaIndex (random-access FASTA)
FastaIndex provides random-access region fetches over a FASTA file, backed by an
.fai index (built on first open — the directory must be writable then).
import pygenogrove as pg
fa = pg.FastaIndex("genome.fa")
fa.fetch("chr1", 1000, 2000) # bases of the 0-based half-open region [1000, 2000)
fa.fetch("chrM") # the whole sequence
fa.sequence_length("chr1") # length in bases
list(fa.names()), "chr1" in fa, len(fa)
# fetch a feature's bases: GenomicCoordinate is closed, fetch is half-open
gc = pg.GenomicCoordinate("+", 4, 7)
fa.fetch("chr1", gc.start, gc.end + 1)
- Methods:
fetch(name, start, end)/fetch(name),sequence_count(),sequence_name(i),sequence_length(name),has_sequence(name), plus the Pythoniclen()/in/names(). Unknown name / invalid region raiseIndexError.
FiletypeDetector (format detection)
FiletypeDetector infers a file's format and compression from its extension
(compression extension stripped first) and magic bytes.
import pygenogrove as pg
ftype, comp = pg.FiletypeDetector().detect_filetype("peaks.bed.gz")
# (Filetype.BED, CompressionType.GZIP)
Filetype:BED/BEDGRAPH/GFF/GTF/VCF/SAM/BAM/FASTA/FASTQ/GG/UNKNOWN.CompressionType:NONE/GZIP/BZIP2/XZ/ZSTD/LZ4/UNKNOWN.
Registry
A process-wide singleton that interns a string identity into a small, stable integer id (deduplicated), mapping it to any JSON-serializable payload — handy for collapsing repeated gene ids, chromosome names, or sources into a 4-byte id plus a single stored record.
import pygenogrove as pg
r = pg.Registry.instance()
# plain string interning — the string is its own payload
a = r.intern("chr1") # 0
r.intern("chr1") # 0 (deduplicated)
r.get(a) # "chr1"
# key -> JSON payload (first write wins on re-intern)
g = r.intern("ENSG001", {"name": "BRCA2", "biotype": "protein_coding"})
r.get(g) # {"name": "BRCA2", "biotype": "protein_coding"}
r.find("ENSG001") # g
r.find("missing") # None
r.serialize("genes.gg") # also: Registry.deserialize(path), reset(), null_id
Current Status
Currently exposed features:
- Strand-aware coordinates —
GenomicCoordinateis the standard key ('+'/'-'/'.'/'*'); overlap and flanking are strand-aware - Universal
Grove(grove<genomic_coordinate, json>) storing arbitrary JSON payloads (dict / list / scalar /None), or no payload at all - Insert / query, multi-index support (per chromosome)
- Graph overlay (directed edges, external keys), including labelled edges on the universal
Grove—add_edge(s, t, data)/get_edges/get_edge_list/get_neighbors_if/link_with— and edge cleanup / bulk linking on every grove (remove_edges_from/to,remove_all_edges,remove_edges_if,clear_graph,graph_empty,link_if) - Key removal + storage compaction:
remove_key(),compact(),vertex_count()/external_vertex_count()/key_storage_size() - Serialization / deserialization to compressed
.ggfiles (an edgeless JSON Grove.ggis readable by a C++grove<genomic_coordinate, std::string>; with labelled edges,grove<genomic_coordinate, std::string, std::string>) - SIF export —
to_sif(path)writes the grove's B+ tree structure and graph-overlay edges as a SIF (Simple Interaction Format) text file for visualization (e.g. Cytoscape) - Nearest-neighbour queries:
flanking()(predecessor / successor), incl. a predicate-filtered overload (e.g. same-strand neighbours) - Point key types —
Numeric(integer keys: ids / timestamps) andKmer(2-bit-encoded DNA k-mers, k ≤ 32, a membership dictionary), each with its ownNumericGrove/KmerGrovecarrying the same universal surface (optional JSON payload, labelled edges, serialization). Overlap is exact equality - Typed data groves for C++ interop:
BedGrove(grove<genomic_coordinate, bed_entry>) andGffGrove(grove<genomic_coordinate, gff_entry>), with theBedEntry/GffEntryvalue types - File readers:
BedReader,GffReader,BamReader(SAM/BAM),FastaReader(FASTA/FASTQ),VcfReader(VCF/BCF — variant records with INFO + per-sample genotypes), plusFastaIndex(random-access) andFiletypeDetector(format detection) - Fast-path inserts on the typed groves:
insert_sorted/insert_bulk, plus entry-derivinginsert(index, entry)/insert_bulk(index, entries)that derive a stranded key from a BED/GFF record's native coordinates Registry— interning singleton mapping a string identity to any JSON payload (plain string interning via single-argintern)
Not yet exposed (tracked in #1):
- BAM CIGAR-element detail, mate info, and aux tags
Performance Tips
- Choose appropriate order: Higher order (e.g., 100-500) reduces tree height for large datasets
- Separate by chromosome: Use the index parameter to maintain separate trees per chromosome
- Query specific indices: Query specific chromosomes instead of all indices when possible
License
This project inherits the license from the genogrove C++ library and is therefore licensed under the GPLv3 license.
Related Projects
- genogrove: The underlying C++ library
Citation
If you use pygenogrove in your research, please cite the original genogrove library
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pygenogrove-0.6.2.tar.gz.
File metadata
- Download URL: pygenogrove-0.6.2.tar.gz
- Upload date:
- Size: 2.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60bc9e2ee0c115aeeeec340d727ce5a4706053cdd5b14ca7d365b71846511a6a
|
|
| MD5 |
c81517a78ae2a58c483c94254b7146fd
|
|
| BLAKE2b-256 |
19702f1d6d3728f53110e9b2a058d862f6ee0b9e85f2973825a58f9af90e83ea
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2.tar.gz:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2.tar.gz -
Subject digest:
60bc9e2ee0c115aeeeec340d727ce5a4706053cdd5b14ca7d365b71846511a6a - Sigstore transparency entry: 1976703864
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 7.9 MB
- Tags: CPython 3.13, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
233da2255bb1226deeb7ce46927b4b9eead4f01b53005a09dce323f7317f2713
|
|
| MD5 |
1f9c30135922c040676f761abe624c2b
|
|
| BLAKE2b-256 |
2cfd92deac225dd11bc4260f4a13d5ee0b2a761197dd58cc24781d6ee4d50ac7
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
233da2255bb1226deeb7ce46927b4b9eead4f01b53005a09dce323f7317f2713 - Sigstore transparency entry: 1976705313
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp313-cp313-macosx_14_0_arm64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp313-cp313-macosx_14_0_arm64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.13, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e31327a80a0992074567bcaeaa280b548010961dac6991ba020a5fbcf85a189
|
|
| MD5 |
1cea57bce1c88e482767282b9ba52238
|
|
| BLAKE2b-256 |
d404e2bde74cd0ad2289522c400195cca38fa24dffd658399f997cd4fae17412
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp313-cp313-macosx_14_0_arm64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp313-cp313-macosx_14_0_arm64.whl -
Subject digest:
9e31327a80a0992074567bcaeaa280b548010961dac6991ba020a5fbcf85a189 - Sigstore transparency entry: 1976703969
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 7.9 MB
- Tags: CPython 3.12, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3df1911778c2e5c1f1448f8fa4dd597ce9c8b2741151d93c5bc272e9502ed521
|
|
| MD5 |
c549814a2c8b19cd6b05f6acbd41ed5e
|
|
| BLAKE2b-256 |
9ef9d6bf1ad572f37bb2aece17e17fe839432418cc73d700eaf4c130a2904588
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
3df1911778c2e5c1f1448f8fa4dd597ce9c8b2741151d93c5bc272e9502ed521 - Sigstore transparency entry: 1976704075
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp312-cp312-macosx_14_0_arm64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp312-cp312-macosx_14_0_arm64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.12, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49ef653ccce4131112e7e13823121b3c3bb1e1db7a1bb30bc8a8f09f8cd92096
|
|
| MD5 |
485ecd46040eed338c6925072f8b2c71
|
|
| BLAKE2b-256 |
1d06204fce5f2fcb16bb4424a34392f1c8afd0ef7e8f387e990ad0036da4eea0
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp312-cp312-macosx_14_0_arm64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp312-cp312-macosx_14_0_arm64.whl -
Subject digest:
49ef653ccce4131112e7e13823121b3c3bb1e1db7a1bb30bc8a8f09f8cd92096 - Sigstore transparency entry: 1976704345
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 7.9 MB
- Tags: CPython 3.11, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
530f2488e7956ac8367e671c3838c5f0b59c5b24eb39de161fd7c304134b31b0
|
|
| MD5 |
169b653d50e5492a2d415e8445e514dc
|
|
| BLAKE2b-256 |
3bf55a4bf48a223eac4989d464be2dd63c2c6053a3dfeabcd094a50e84d192c5
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
530f2488e7956ac8367e671c3838c5f0b59c5b24eb39de161fd7c304134b31b0 - Sigstore transparency entry: 1976705071
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp311-cp311-macosx_14_0_arm64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp311-cp311-macosx_14_0_arm64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.11, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e4fdb8ff6eee672209a100714f9dbf71cd1a1eddadaa36649fe61a5d21f990f
|
|
| MD5 |
78ea3ee52bf241bf23904a157fffff0e
|
|
| BLAKE2b-256 |
e1c85471c54f43e85834c727d67f03201470f004899e332e994765b532569bc0
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp311-cp311-macosx_14_0_arm64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp311-cp311-macosx_14_0_arm64.whl -
Subject digest:
6e4fdb8ff6eee672209a100714f9dbf71cd1a1eddadaa36649fe61a5d21f990f - Sigstore transparency entry: 1976704187
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 7.9 MB
- Tags: CPython 3.10, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a8ca31855960f9d014f4658255ccd70c5711a4cf76c041c80dee369572b391c
|
|
| MD5 |
8f7265941804464dc3aabc49f12987d7
|
|
| BLAKE2b-256 |
7a1af58f0b57594225a9db93cdb973e56e025d3509ee559523e920d3b16fc0e8
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
4a8ca31855960f9d014f4658255ccd70c5711a4cf76c041c80dee369572b391c - Sigstore transparency entry: 1976704927
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp310-cp310-macosx_14_0_arm64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp310-cp310-macosx_14_0_arm64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.10, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
407c15c99e8198b41e170fbb7ff95f681ab2ec550241d64eeb36b2a5f376408c
|
|
| MD5 |
e2c3c3566d7b590c1972c11795d42a1a
|
|
| BLAKE2b-256 |
71a725e9eee10d0d39a3bdd5bd31bf6e6759e2210beb1cbe849dd23b9798e7b3
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp310-cp310-macosx_14_0_arm64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp310-cp310-macosx_14_0_arm64.whl -
Subject digest:
407c15c99e8198b41e170fbb7ff95f681ab2ec550241d64eeb36b2a5f376408c - Sigstore transparency entry: 1976704576
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 7.9 MB
- Tags: CPython 3.9, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e83f40213437f040b1df6ec26775b8d7cc432e9a70c697c6bfde867f8ba86f3
|
|
| MD5 |
3bc496da300180a25a8c57bb7805abf2
|
|
| BLAKE2b-256 |
09074f13097e5452e8a62dfbfe140c06a10f37638113ea124eaee52f3b6856f5
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
1e83f40213437f040b1df6ec26775b8d7cc432e9a70c697c6bfde867f8ba86f3 - Sigstore transparency entry: 1976704747
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pygenogrove-0.6.2-cp39-cp39-macosx_14_0_arm64.whl.
File metadata
- Download URL: pygenogrove-0.6.2-cp39-cp39-macosx_14_0_arm64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.9, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6bc36e93b0396e2c5f2e8e78d0d7e82a2216deec922a69b637c1f9fd4f2b2265
|
|
| MD5 |
8e3054d2d2c210c82ecd3ac107c63bf0
|
|
| BLAKE2b-256 |
b4d0ddca1be5f3e2fdbe36e5dac5a53a9ea9976a0b25bd6efd236fa3a670f9b6
|
Provenance
The following attestation bundles were made for pygenogrove-0.6.2-cp39-cp39-macosx_14_0_arm64.whl:
Publisher:
release.yml on genogrove/pygenogrove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygenogrove-0.6.2-cp39-cp39-macosx_14_0_arm64.whl -
Subject digest:
6bc36e93b0396e2c5f2e8e78d0d7e82a2216deec922a69b637c1f9fd4f2b2265 - Sigstore transparency entry: 1976705180
- Sigstore integration time:
-
Permalink:
genogrove/pygenogrove@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/genogrove
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@56602a4aef8059cd4bf31d34ac80e5a868c0a122 -
Trigger Event:
push
-
Statement type: