checkbio
A static analysis linter that catches bioinformatics-specific mistakes in Python code — especially the kind AI coding assistants (Claude, Codex, ChatGPT, Copilot, etc.) tend to silently introduce.
It does not try to be a general-purpose linter. Use flake8/pylint/
ruff for that. checkbio only checks for domain-specific correctness
issues that a general linter has no way of knowing about: genomic
coordinate bugs, hallucinated or deprecated Biopython/pysam API usage, and
file format mismatches.
Why
AI coding assistants are very good at producing plausible-looking
bioinformatics code, and just as good at confidently getting
domain-specific details wrong: mixing 0-based and 1-based coordinate
systems, calling a Biopython API that was removed years ago, passing an
invalid mode string to pysam.AlignmentFile. These bugs are usually
silent — the code runs, sometimes even produces output, and just happens
to be wrong. checkbio exists to catch the specific patterns that keep
showing up.
Install
pip install checkbio
From source
git clone https://github.com/Affogat0/checkbio.git
cd checkbio
pip install -e .
Usage
checkbio script.py
checkbio pipeline/*.py
Example output:
examples/bad_example.py
12:0 [BIO001] 'Bio.Alphabet' was removed in Biopython 1.78 (2020)...
15:11 [REF001] Found an hg19/GRCh37 reference and an hg38/GRCh38 reference in the same file...
19:13 [LOC003] Found chromosome name 'chr1' and 'MT' in the same file...
25:14 [BIO002] 'fastq-generic' is not a recognized Bio.SeqIO format string...
31:11 [PYS001] 'read' is not a valid pysam.AlignmentFile mode string...
37:11 [PYS002] '.fetch()' requires an index file (.bai/.csi/.crai)...
43:11 [PYS003] '.fetch()' called with argument(s) ['vcf_pos'] that look 1-based...
49:7 [LOC001] Comparing a 0-based coordinate directly against a 1-based coordinate...
57:11 [LOC002] Slicing with genomic-position-like variable(s) ['start', 'end']...
62:11 [TAB001] Merge key(s) ['position'] include a genomic position column but no chromosome column...
68:4 [CLI001] Call to 'samtools' via subprocess does not verify the command succeeded...
checkbio: 3 error(s), 9 warning(s)
As a pre-commit hook
Add to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/Affogat0/checkbio
rev: v0.1.0
hooks:
- id: checkbio
Rule families
Rules are grouped into families by ID prefix, following the same pattern as tools like ESLint/Ruff. This will grow well past v0.1 — the prefix tells you at a glance what domain a warning belongs to without needing to memorize individual rule numbers.
| Prefix | Family | Status |
|---|---|---|
LOC |
Genomic coordinates | implemented |
REF |
Reference genome builds | implemented |
BIO |
Biopython-specific | implemented |
PYS |
pysam-specific | implemented |
TAB |
Genomic tables (pandas) | implemented |
CLI |
External bioinformatics tools (subprocess) | implemented |
REP |
Reproducibility / robustness (e.g. unparseable file) | implemented |
FMT |
General file format mismatches | planned |
VCF |
Variant (VCF) processing | planned |
SEQ |
Sequence handling (translation frames, ambiguous bases) | planned |
SAM |
SAM/BAM/CRAM-specific (beyond pysam wrapper calls) | planned |
ID |
Biological identifiers (Ensembl versions, gene symbols) | planned |
NET |
NCBI / remote API etiquette | planned |
AI |
Suspicious AI-generated-code patterns (cross-cutting) | planned |
Rules (v0.1)
| ID | Severity | What it catches |
|---|---|---|
| LOC001 | warning | Comparing a 0-based (BED-style) coordinate directly against a 1-based (VCF/GFF/SAM-style) coordinate with no visible offset |
| LOC002 | warning | Slicing a sequence with a genomic start/end-like variable, no nearby indication of coordinate convention |
| LOC003 | warning | A file uses both chr-prefixed and bare chromosome names (or chrM/MT) — a common silent join/comparison failure |
| REF001 | warning | A file references both hg19/GRCh37 and hg38/GRCh38 resources — coordinates from the two builds aren't interchangeable |
| BIO001 | error | Import of Bio.Alphabet, removed in Biopython 1.78 |
| BIO002 | error | SeqIO/AlignIO call with a format string that isn't a real Biopython format |
| PYS001 | error | pysam.AlignmentFile() opened with an invalid mode string |
| PYS002 | warning | .fetch()/.pileup() call — reminder that this requires an index file to exist |
| PYS003 | warning | .fetch()/.pileup() called with a start/end argument that looks 1-based, with no visible -1 conversion — pysam expects 0-based, half-open coordinates |
| TAB001 | warning | DataFrame.merge() keyed on a genomic position column with no accompanying chromosome column |
| CLI001 | warning | subprocess.run()/.call() to a known bioinformatics tool (samtools, bcftools, bedtools, ...) with no verification the command succeeded |
| REP000 | error | The file itself couldn't be parsed (syntax error) |
Known limitations (v0.1):
- PYS002 flags every
.fetch()/.pileup()call, even ones already correctly guarded by an index check. Real guard-detection needs data-flow analysis (on the roadmap) — for now it's a reminder, not a definitive error. - LOC003 and REF001 are file-level heuristics based on string literals — they won't catch mismatches that only appear via runtime values (e.g. a chromosome name read from a config file at runtime), and a deliberate liftover step will trigger a (correct, but not-actually-a-bug) flag.
- TAB001 only inspects string literal merge keys — it won't catch
keys built dynamically (e.g. a list constructed in a variable before
being passed to
on=).
Roadmap
- Data-flow-aware PYS002 (stop flagging already-guarded
.fetch()calls) VCFfamily: multi-allelic assumptions, genotype/INFO field presence, variant normalization before comparisonSEQfamily: translation frame mistakes, ambiguous base (IUPAC) assumptions, sequence-type confusion (protein vs. nucleotide)IDfamily: Ensembl ID version suffix mismatches, gene symbol vs. stable ID joins, species mismatchesNETfamily: NCBI/Entrez rate limiting, missing email/API key, retry/backoff on remote calls- File-extension vs. parser-library mismatch detection (e.g. calling a
FASTA parser on a
.vcfpath) - GitHub Action for CI-time checking on pull requests
- VS Code extension for real-time inline flagging
Contributing
This project exists to catch real, recurring mistakes — if you've hit a bioinformatics-specific bug that an AI coding assistant introduced (or that you've seen a colleague hit), please open an issue describing it. Concrete before/after code examples are the most useful thing you can contribute.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 checkbio-0.1.0.tar.gz.
File metadata
- Download URL: checkbio-0.1.0.tar.gz
- Upload date:
- Size: 17.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bcfcde71b32466ecf1c53cc2162572dcb079b3c4231920270f614c6575eb80c
|
|
| MD5 |
b0a2cf8bd9007f84638ab72e3793de8d
|
|
| BLAKE2b-256 |
9a39f2d3b87a873365d389f2c2e334de224b52407201faab85b25e5eae8c92a0
|
File details
Details for the file checkbio-0.1.0-py3-none-any.whl.
File metadata
- Download URL: checkbio-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b9098534a0e0db950632a168ff5404957e8623dabf24b05a97598f9376a3a48
|
|
| MD5 |
5ddf8d6284b7901be2b3e523afc4834d
|
|
| BLAKE2b-256 |
bc750708faa0b8e3f73e6ad9a00bed397ee2d04f628786417563fbdad10cf197
|