escapepod is a Python package for reading and writing POD5 files, backed by
the same Rust engine as the escpod CLI. It mirrors the API of Oxford
Nanopore's official pod5
package closely enough to be a mostly drop-in replacement, while reading and
writing considerably faster.
import escapepod
with escapepod.Reader("experiment.pod5") as reader:
print(f"{reader.read_count} reads")
for read in reader:
signal = reader.get_signal(read) # raw ADC (int16)
print(read.read_id, read.num_samples, signal.mean())uv pip install escapepodWheels are published for CPython 3.9+ (abi3) on Linux (x86_64/aarch64, manylinux + musllinux) and macOS (x86_64/arm64).
To build against a checkout of the repository (or on a platform without a prebuilt wheel), use maturin:
uv pip install maturin
maturin develop --release --manifest-path crates/escapepod-python/Cargo.tomlmaturin develop compiles the extension and installs it as escapepod in the
active virtualenv/conda env. Drop --release for a faster (unoptimized) build
while iterating.
You can also install straight from Git once you have a Rust toolchain and maturin available:
uv pip install "git+https://github.com/rnabioco/escapepod-rs.git#subdirectory=crates/escapepod-python"Verify the install:
import escapepod
print(escapepod.__version__)| Object | Purpose |
|---|---|
Reader |
Read reads, metadata, and signal from a single POD5 file |
DatasetReader |
Read a directory or list of files as one logical dataset |
Writer |
Create new POD5 files |
ReadData |
A single read's metadata |
RunInfo / create_run_info |
Acquisition/run metadata |
| Signal processing | normalize_signal, mad_normalize, refine_signal_map, KmerTable |
Pod5Error |
Raised on malformed/invalid POD5 data |
The API is intentionally close to the official package. The main differences:
| Task | pod5 |
escapepod |
|---|---|---|
| Open a file | pod5.Reader(path) |
escapepod.Reader(path) |
| Open a directory | pod5.DatasetReader(path) |
escapepod.DatasetReader(path) |
| Iterate reads | for read in reader.reads(): |
for read in reader: (or reader.reads()) |
| Raw signal | read.signal |
reader.get_signal(read) |
| Signal in pA | read.signal_pa |
reader.get_signal_pa(read) |
| Reads → DataFrame | (manual) | reader.to_pandas() / reader.to_polars() |
The most important structural difference: signal is not attached to the
ReadData object. You request it from the reader with get_signal(read) (raw
ADC int16) or get_signal_pa(read) (calibrated picoamps float32). Keeping
metadata and signal separate lets you scan every read's metadata without paying
to decode signal you may not need.
See Reading Files and Writing Files for the full surface, and Signal Processing for the analysis helpers.
Invalid or corrupt POD5 data raises escapepod.Pod5Error; ordinary I/O
problems (missing file, permissions) raise the usual OSError/IOError:
import escapepod
try:
reader = escapepod.Reader("experiment.pod5")
except escapepod.Pod5Error as e:
print(f"Not a valid POD5 file: {e}")
except OSError as e:
print(f"Could not open file: {e}")