fgmetric
Type-validated Python models for delimited data files.
Visit us at Fulcrum Genomics to learn more about how we can power your Bioinformatics with fgmetric and beyond.
Overview
fgmetric lets you define Python classes ("Metrics") that map directly to rows in CSV/TSV files.
It handles parsing, type coercion (strings → int, float, bool), and validation automatically using Pydantic.
Installation
Requires Python 3.12 or later.
pip install fgmetric
Or with uv:
uv add fgmetric
Why fgmetric?
If you're a bioinformatician or data engineer processing delimited files in Python, you've probably written code like this:
import csv
with open("metrics.tsv") as f:
reader = csv.DictReader(f, delimiter="\t")
for row in reader:
quality = int(row["mapping_quality"])
is_duplicate = row["is_duplicate"].lower() in ("true", "1", "yes")
if row["score"]: # handle empty strings
score = float(row["score"])
# ... repeat for every field
fgmetric replaces this with:
for metric in AlignmentMetric.read(path):
# metric.mapping_quality is already an int
# metric.is_duplicate is already a bool
# metric.score is already Optional[float]
How it compares:
- vs. csv + dataclasses — Automatic type coercion and validation without boilerplate. Built on Pydantic, so additional custom validators and serializers can be readily added.
- vs. pandas — Unlike pandas,
fgmetricprocesses records lazily — you can handle files larger than memory. AndMetrics are type-validated and can be made immutable, making them safe to pass between functions without defensive copying. - vs. Pydantic alone —
fgmetrichandles CSV/TSV specifics (header parsing, delimiter configuration) and provides out-of-the box features like empty value handling and Counter field pivoting.
Quick Start
Define a class to represent each row:
from fgmetric import Metric, MetricReader, MetricWriter
class AlignmentMetric(Metric):
read_name: str
mapping_quality: int
is_duplicate: bool = False
Then read or write:
# Reading
for metric in AlignmentMetric.read("alignments.tsv"):
print(f"{metric.read_name}: MQ={metric.mapping_quality}")
# Writing
metrics = [
AlignmentMetric(read_name="read1", mapping_quality=60),
AlignmentMetric(read_name="read2", mapping_quality=30, is_duplicate=True),
]
with MetricWriter.open(AlignmentMetric, "output.tsv") as writer:
writer.writeall(metrics)
Metric.read() reads the whole file into a list. To stream metrics one at a time without holding them all in memory, use MetricReader.open():
# Streaming from a path
with MetricReader.open(AlignmentMetric, "alignments.tsv") as reader:
for metric in reader:
print(f"{metric.read_name}: MQ={metric.mapping_quality}")
To read from an open file handle or any other text IO source (e.g. StringIO), use MetricReader directly:
# Reading from an IO source
with open("alignments.tsv") as handle:
reader = MetricReader(AlignmentMetric, handle)
for metric in reader:
print(f"{metric.read_name}: MQ={metric.mapping_quality}")
Example input file (alignments.tsv):
read_name mapping_quality is_duplicate
read1 60 false
read2 30 true
Invalid data raises pydantic.ValidationError with details about which field failed.
Core Usage
Delimiters
The field delimiter is inferred from the file extension, so common formats need no configuration: .csv is comma-delimited, while .tsv, .txt, .tab, and Picard-style *metrics files (e.g. .insert_size_metrics) are tab-delimited. A trailing compression suffix (.gz, .bz2, .xz) is ignored, so data.csv.gz is still comma-delimited.
# Comma-delimited, inferred from the .csv extension
for metric in MyMetric.read("data.csv"):
...
Pass delimiter= to override inference, or to read or write a file whose extension isn't recognized. An unrecognized extension with no explicit delimiter raises ValueError.
# Pipe-delimited file with an unrecognized extension
with MetricWriter.open(MyMetric, "output.dat", delimiter="|") as writer:
...
Compression
Reading and writing transparently handle gzip, bzip2, and xz files based on the path extension (via xopen):
for metric in AlignmentMetric.read("alignments.tsv.gz"):
...
with MetricWriter.open(AlignmentMetric, "output.tsv.bz2") as writer:
writer.writeall(metrics)
List Fields
Fields typed as list[T] are automatically parsed from and serialized to delimited strings:
class TaggedRead(Metric):
read_id: str
tags: list[str] # "A,B,C" becomes ["A", "B", "C"]
scores: list[int] # "1,2,3" becomes [1, 2, 3]
optional_tags: list[str] | None # "" becomes None
The list delimiter defaults to , but can be customized per-metric:
class SemicolonMetric(Metric):
collection_delimiter = ";"
values: list[int] # "1;2;3" becomes [1, 2, 3]
Counter Fields
When your file has categorical data with one column per category (e.g. base counts A, C, G, T), you can model them as a single Counter[StrEnum] field:
from collections import Counter
from enum import StrEnum
from fgmetric import Metric
class Base(StrEnum):
A = "A"
C = "C"
G = "G"
T = "T"
class BaseCountMetric(Metric):
position: int
counts: Counter[Base]
# Input TSV:
# position A C G T
# 1 10 5 3 2
# Parses to:
# BaseCountMetric(position=1, counts=Counter({Base.A: 10, Base.C: 5, ...}))
Contributing
See the contributing guide for development setup and testing instructions.
Metadata
Release files for fgmetric 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fgmetric-0.4.0.tar.gz | 75.3 kB | With provenance | Details for fgmetric-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fgmetric-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for fgmetric-0.4.0-py3-none-any.whl |
Total release size: 96.9 kB
Release files / fgmetric-0.4.0.tar.gz
| Download URL | fgmetric-0.4.0.tar.gz |
|---|---|
| Size | 75.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
77b6d5cfa9fd4d8f6ec9d25c25baaabafdb409a2692dc27517048ff23a9bc725
|
|
BLAKE2b-256 checksum How to use checksums |
08619267d82a73a1b7dfdd001bb0d441f386115176e20cad37c1d699933bea0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 15, 2026.
Transparency logRelease files / fgmetric-0.4.0-py3-none-any.whl
| Download URL | fgmetric-0.4.0-py3-none-any.whl |
|---|---|
| Size | 21.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4f84431a54cfa88eea0f5634315f02b733d8f6049aa7f4e74c21dc494d772911
|
|
BLAKE2b-256 checksum How to use checksums |
c7f1af4a086db19b78d103207683e871d1be0ac52a7e8d7fc9f18fe2c021c787
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 15, 2026.
Transparency logSecurity for fgmetric 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fgmetric-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 15, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.