clean-fasta
A small, dependency-free command-line tool (and Python library) for cleaning
and filtering FASTA sequence files. It strips gaps and whitespace, drops
sequences that are too short, too long, malformed, or below a valid-character
threshold, optionally removes duplicate identifiers, and writes tidy,
line-wrapped FASTA output — along with a before/after statistics report of
what happened (length distribution, N50/L50, GC content, extremes, and more).
It can also profile a file without cleaning it (--stats-only) or preview a run
without writing anything (--dry-run).
What it does
For every sequence in the input, clean-fasta:
- Removes gap characters (
-) and all whitespace from the sequence body. - Collapses runs of whitespace in the identifier line.
- Drops the sequence if it:
- has an empty identifier;
- contains digits (unless
--allow-digits); - is shorter than
--min-length(default20); - is longer than
--max-length(if set); - has a valid-character ratio below
--min-ratio(default0.99); - has a duplicate identifier (unless
--no-unique).
- Writes the surviving sequences, wrapped at
--wrapcharacters (default80).
The valid-character ratio is:
- for
--type aa: the fraction of characters that are not_ - ? X x * .; - for
--type na: the fraction of characters that areA,C,G, orT.
Installation
Requires Python 3.9+.
pip install clean-fasta
This installs a clean-fasta command on your PATH. Verify it with:
clean-fasta --version
To install the latest development version from source instead:
git clone https://github.com/cmzmasek/clean-fasta
cd clean-fasta
pip install .
# or, for development (editable install + test/lint tools)
pip install -e ".[dev]"
Usage
clean-fasta [OPTIONS] INPUT [OUTPUT]
Use - for INPUT or OUTPUT to read from stdin / write to stdout. OUTPUT
is required for a normal run, but not with --dry-run or --stats-only.
Options
| Option | Default | Description |
|---|---|---|
-t, --type {aa,na} |
aa |
Sequence type: amino acid or nucleic acid. |
-m, --min-length N |
20 |
Minimum sequence length. |
-M, --max-length N |
none | Maximum sequence length. |
-r, --min-ratio R |
0.99 |
Minimum ratio of valid characters (0.0–1.0). |
-u, --unique / --no-unique |
--unique |
Drop / keep sequences with duplicate ids. |
--allow-digits |
off | Keep sequences that contain digit characters. |
-w, --wrap N |
80 |
Wrap output lines at N chars (0 = no wrapping). |
-f, --force |
off | Overwrite the output file if it exists. |
-q, --quiet |
off | Suppress the summary report. |
-n, --dry-run |
off | Analyze and report, but write no output. |
--stats-only |
off | Print statistics for INPUT only; write nothing (no OUTPUT needed). |
--version |
Print the version and exit. |
The summary report is written to stderr, so piping via stdout stays clean.
Statistics report
Every run (unless --quiet) prints a before/after report: descriptive
statistics over the input set next to the same statistics over the kept
(surviving) set. This includes sequence count, total residues, the length
distribution (min / Q1 / median / Q3 / max / mean / stddev), N50 and
L50, GC content (nucleic-acid input only), the overall valid-character
ratio, and the named longest / shortest / most- and least-"bad"-character
sequences.
# Profile a file without cleaning it (no OUTPUT argument needed)
clean-fasta genes.fasta --stats-only -t na
# Preview what a cleaning run would do, writing nothing
clean-fasta genes.fasta genes.clean.fasta --dry-run -t na -m 200
Examples
# Clean a protein FASTA, keeping sequences >= 50 residues
clean-fasta proteins.fasta proteins.clean.fasta -m 50
# Nucleotide sequences, allow up to 2% ambiguous bases, cap length at 5000
clean-fasta genes.fasta genes.clean.fasta -t na -r 0.98 -M 5000
# Use in a pipeline (read stdin, write stdout, no report)
gunzip -c raw.fasta.gz | clean-fasta - - -q > clean.fasta
# Overwrite an existing output file
clean-fasta in.fasta out.fasta --force
Use as a library
The filtering logic is I/O-free and importable:
from clean_fasta import stream_fasta, filter_sequences, CleanStats
stats = CleanStats()
with open("in.fasta") as handle:
for seq in filter_sequences(stream_fasta(handle), min_length=50, stats=stats):
print(seq.to_fasta_wrapped(80))
print("passed:", stats.passed, "of", stats.total)
Or clean a file in one call:
from clean_fasta import clean_fasta_file
stats = clean_fasta_file("in.fasta", "out.fasta", min_length=50, is_aa=True, force=True)
Development
pip install -e ".[dev]"
pytest # run the test suite
ruff check . # lint
Releasing (maintainers)
Releases are published to PyPI automatically by
.github/workflows/release.yml using PyPI
trusted publishing — no API tokens
are stored anywhere.
To cut a release:
- Bump
__version__insrc/clean_fasta/__init__.pyand updateCHANGELOG.md. - Commit and push to
main. - Create a GitHub Release whose tag is
v<version>(e.g.v2.0.1).
Publishing the release triggers the workflow, which builds the sdist and wheel and uploads them to PyPI. The workflow fails fast if the release tag does not match the packaged version, so a forgotten version bump can't ship.
License
MIT © Christian M. Zmasek
Metadata
Release files for clean-fasta 2.1.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 | |
|---|---|---|---|
| clean_fasta-2.1.0.tar.gz | 19.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| clean_fasta-2.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.5 kB
Release files / clean_fasta-2.1.0.tar.gz
| Download URL | clean_fasta-2.1.0.tar.gz |
|---|---|
| Size | 19.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
990c5a9298fc51fd1991fb61b90d64c0270ebc4874f9e1b489016a17ee0c9ad3
|
|
BLAKE2b-256 checksum How to use checksums |
e8530e6a463400803c395c7d682f251cff5408aa7331b60da6925dd924188b39
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 16, 2026.
Transparency logRelease files / clean_fasta-2.1.0-py3-none-any.whl
| Download URL | clean_fasta-2.1.0-py3-none-any.whl |
|---|---|
| Size | 16.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
74635eeebc5dac04dbb68d0d0206fb76cf5f77ece10d4c34a463a1db57b76e7c
|
|
BLAKE2b-256 checksum How to use checksums |
93e465d1c0f3800d2ee87c3c767edeac7e60fd5e452aba5957395e20acddeb5e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 16, 2026.
Transparency log