Metadata-Version: 2.5
Name: fastatacular
Version: 1.0.0
Summary: A pure-Python library for reading and writing FASTA sequence files.
Project-URL: Homepage, https://github.com/tacular-omics/fastatacular
Project-URL: Documentation, https://github.com/tacular-omics/fastatacular#readme
Project-URL: Repository, https://github.com/tacular-omics/fastatacular
Project-URL: Issues, https://github.com/tacular-omics/fastatacular/issues
Project-URL: Changelog, https://github.com/tacular-omics/fastatacular/blob/main/CHANGELOG.md
Author-email: Patrick Garrett <pgarrett@scripps.edu>
Maintainer-email: Patrick Garrett <pgarrett@scripps.edu>
License-Expression: MIT
License-File: LICENSE
Keywords: bioinformatics,fasta,genomics,proteomics,sequence
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# fastatacular

[![PyPI](https://img.shields.io/pypi/v/fastatacular)](https://pypi.org/project/fastatacular/)
[![Python Package](https://github.com/tacular-omics/fastatacular/actions/workflows/ci.yml/badge.svg)](https://github.com/tacular-omics/fastatacular/actions/workflows/ci.yml)
[![License](https://img.shields.io/github/license/tacular-omics/fastatacular)](https://github.com/tacular-omics/fastatacular/blob/main/LICENSE)
[![Python](https://img.shields.io/pypi/pyversions/fastatacular)](https://pypi.org/project/fastatacular/)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22926358.svg)](https://doi.org/10.5281/zenodo.22926358)

A small, dependency-free library for reading and writing [FASTA](https://en.wikipedia.org/wiki/FASTA_format) sequence files in Python. It's built for proteomics and genomics pipelines that need fast, predictable FASTA parsing without pulling in a bioinformatics megapackage.

It understands UniProt-style description keys (`OS=`, `OX=`, `GN=`, `PE=`, `SV=`) and pipe-delimited identifiers (`sp|P12345|EX_HUMAN`, `gi|12345|ref|NP_000001.1|`) out of the box, so you get structured fields instead of a header string to parse yourself.

## Highlights

- **Zero dependencies** — pure Python, nothing else to install.
- **Two ways to read** — `read_fasta` for the whole file at once, `FastaReader` to stream entries lazily without loading everything into memory.
- **UniProt headers parsed for you** — accession, organism, gene name, protein existence, and sequence version come back as typed fields, not a string you have to split yourself.
- **Round-trip safe** — entries produced by `read_fasta` write back out byte-for-byte compatible headers.
- **Actionable parse errors** — `FastaParseError` reports the offending line number and surrounding context.
- **Shares its API shape with [pefftacular](https://github.com/tacular-omics/pefftacular)**, the PEFF (PSI Extended FASTA) sibling library, so switching formats doesn't mean relearning the interface.

## Install

```bash
pip install fastatacular
```

Dev install:

```bash
just install
```

## Quick start

**read_fasta** — load everything into memory at once:

```python
from fastatacular import read_fasta

entries = read_fasta("proteins.fasta")
for entry in entries:
    print(entry.identifier, len(entry.sequence))
```

**FastaReader** — iterate lazily without loading the full file:

```python
from fastatacular import FastaReader

with FastaReader("proteins.fasta") as reader:
    for entry in reader:
        process(entry)
```

## Data model

Each entry is a `SequenceEntry`:

| Field | Type | Description |
|---|---|---|
| `identifier` | `str` | Token immediately after `>` (e.g. `sp|P12345|EX_HUMAN`) |
| `sequence` | `str` | Concatenated sequence with whitespace stripped |
| `prefix` | `str \| None` | Database prefix (`sp`, `tr`, `gi`, ...) when the id is pipe-delimited |
| `accession` | `str \| None` | Second pipe field (e.g. `P12345` in `sp\|P12345\|EX_HUMAN`) |
| `entry_name` | `str \| None` | Third pipe field on UniProt ids (e.g. `EX_HUMAN`) |
| `description` | `str \| None` | Free text after the identifier |
| `pname` | `str \| None` | Protein name (description text, minus `KEY=value` pairs) |
| `gname` | `str \| None` | Gene name (`GN=`) |
| `os_name` | `str \| None` | Organism name (`OS=`) |
| `ncbi_tax_id` | `int \| None` | NCBI taxonomy ID (`OX=`) |
| `pe` | `int \| None` | Protein existence level (`PE=`) |
| `sv` | `int \| None` | Sequence version (`SV=`) |
| `extra` | `dict[str, str]` | Any other `KEY=value` pairs found in the header |
| `raw_header` | `str` | The original header line (without leading `>`) |

## UniProt-style headers

```python
from fastatacular import read_fasta

[entry] = read_fasta("one.fasta")
# >sp|P12345|EX_HUMAN Example protein OS=Homo sapiens OX=9606 GN=EXMP PE=1 SV=2

entry.prefix         # "sp"
entry.accession      # "P12345"
entry.entry_name     # "EX_HUMAN"
entry.pname          # "Example protein"
entry.os_name        # "Homo sapiens"
entry.ncbi_tax_id    # 9606
entry.gname          # "EXMP"
entry.pe             # 1
entry.sv             # 2
```

Non-standard `KEY=value` pairs are captured in `entry.extra`. Headers with no `KEY=value` tokens leave `description` and `pname` populated and `extra` empty.

## Writing

Construct entries and write them out:

```python
from fastatacular import SequenceEntry, write_fasta

entries = [
    SequenceEntry(
        identifier="sp|P12345|EX_HUMAN",
        sequence="MKTIIALSYIFCLVFA",
        pname="Example protein",
        os_name="Homo sapiens",
        ncbi_tax_id=9606,
        gname="EXMP",
        pe=1,
        sv=2,
    ),
]

write_fasta(entries, "output.fasta")
```

`dest` accepts a path string, a `pathlib.Path`, or a text-mode file object.

Sequence lines wrap at 60 characters by default. Override with `line_width=` (pass `0` to disable wrapping):

```python
write_fasta(entries, "output.fasta", line_width=80)
write_fasta(entries, "single-line.fasta", line_width=0)
```

If `raw_header` is set on an entry (as it is on every entry produced by `read_fasta`) and still matches the entry's structured fields, the writer round-trips it verbatim. If you changed a field (for example `dataclasses.replace(entry, gname="XYZ")`), or `raw_header` is empty, the header is rebuilt from the structured fields, so your edit is written.

## Error handling

Parse errors raise `FastaParseError`:

```python
from fastatacular import FastaParseError, read_fasta

try:
    entries = read_fasta("malformed.fasta")
except FastaParseError as e:
    print(e.line)     # offending line number
    print(e.context)  # surrounding line content
```

Write errors raise `FastaWriteError`, whose `index` names the bad entry. Every entry is
validated before anything is written, so a failed `write_fasta` leaves no partial file.
Both errors subclass `FastaError` (a `ValueError`), so `except FastaError` catches either.

`SequenceEntry` is frozen but not hashable (its `extra` field is a dict), so key sets and
dicts by `entry.identifier`, not by the entry. A `FastaReader` is single-pass: iterate it
once, or open a new one to read the file again.

## Development

```bash
just install      # install dependencies
just test         # run tests
just test-v       # run tests (verbose)
just cov          # run tests with coverage
just lint         # ruff lint
just format       # ruff format
just check        # lint + type check + test
just build        # build the package
just clean        # remove cache files
```

## Citation

If you use fastatacular in research, please cite the archived software release. Machine-readable citation metadata is available in [`CITATION.cff`](https://github.com/tacular-omics/fastatacular/blob/main/CITATION.cff); GitHub's **Cite this repository** menu can render it as APA or BibTeX. DOI: [10.5281/zenodo.22926358](https://doi.org/10.5281/zenodo.22926358).

## License

[MIT](https://github.com/tacular-omics/fastatacular/blob/main/LICENSE)
