Reading and writing¶
pefftacular.read_peff
¶
read_peff(
source: str | Path | IO[str] | IO[bytes],
*,
compression: Compression = "infer",
) -> tuple[FileHeader, list[SequenceEntry]]
Convenience: parse an entire PEFF file into header + list of entries.
A path may be plain or gzip/bzip2/xz compressed; compression is described on
:class:PeffReader ("infer" sniffs the magic bytes). Undecodable (non-UTF-8)
or corrupt compressed input raises PeffParseError chained to the cause.
Source code in src/pefftacular/_parser.py
pefftacular.PeffReader
¶
PeffReader(
source: str | Path | IO[str] | IO[bytes],
*,
compression: Compression = "infer",
)
Lazy reader for PEFF files.
Use it as a context manager, like fastatacular.FastaReader: a path is opened
in __enter__ (UTF-8, a leading BOM is skipped) and closed in __exit__; a
stream you pass in is read but never closed.
compression works like the pandas argument of that name. "infer" (the
default) decompresses a gzip, bzip2 or xz path detected from its magic bytes, not
its suffix, and reads an open handle as it is. "gzip", "bz2" or "xz"
forces that format (PeffParseError if the bytes are not in it); an open handle
must then be binary (open(path, "rb")). None reads plain text even if the
bytes look compressed. Any other value raises PeffError. Accessing header or iterating
outside the with block raises :class:RuntimeError. Entries can be
iterated once; a second iteration over the same reader yields nothing.
Example::
with PeffReader("proteins.peff") as reader:
header = reader.header
for entry in reader:
...
Source code in src/pefftacular/_parser.py
to_records
¶
Read the remaining entries as flat dicts (see :func:pefftacular.to_records).
Source code in src/pefftacular/_parser.py
pefftacular.write_peff
¶
write_peff(
header: FileHeader,
entries: Iterable[SequenceEntry],
dest: str | Path | IO[str] | IO[bytes],
*,
verify: bool = True,
compression: Compression = "infer",
) -> None
Write a complete PEFF file.
Every entry is validated and formatted before anything is written to dest:
on a PeffWriteError a path is not created or truncated and nothing is
written to a handle. entries is consumed once, as a stream; formatted text
is spooled to a temporary file past 32 MiB instead of being held in memory.
With verify=True (the default) each formatted entry is parsed back and
compared with the entry, so a value holding PEFF syntax (\Key= text, an
unbalanced paren) raises instead of writing a file that reads back
differently. verify=False skips that read-back (about 3/4 of the write
time); use it for entries that came from read_peff / PeffReader
unchanged, or that you have already written once. The basic checks (empty or
malformed prefix, id or sequence, line breaks, duplicate keys) always run.
compression works like the pandas argument of that name. With "infer" (the
default) a path ending in .gz, .bz2 or .xz (any case) is written
compressed in that format and any other path or an open handle as plain text.
"gzip", "bz2" or "xz" compresses in that format whatever the suffix; an
open handle must then be binary (open(path, "wb")), and it is left open. None
always writes plain text. gzip output has mtime=0, so the same entries give
byte-identical files. read_peff / PeffReader read all of these back.
Source code in src/pefftacular/_writer.py
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 | |
Records¶
to_records turns entries into flat dicts for a data frame (see the README section on
tables). from_records is its inverse and takes the file header, whose CustomKeyDef
declarations are what keep custom key values from being read back as unknown keys.
pefftacular.from_records
¶
from_records(
records: Iterable[Mapping[str, object]],
header: FileHeader | None = None,
) -> list[SequenceEntry]
Build :class:SequenceEntry objects from flat records, the inverse of :func:to_records.
Writes a data frame back to PEFF::
header, entries = read_peff("in.peff")
df = pandas.DataFrame(to_records(entries))
write_peff(header, from_records(df.to_dict("records"), header), "out.peff")
Each record is a mapping with every key in :data:RECORD_KEYS. A key that is not
one of them, or one of them left out, raises: a dropped column is how a caller loses
data without noticing. None, float NaN and the pandas/polars missing markers
mean the entry has no value for that key, so the key is left out of the entry; ""
is an empty value and is kept, because a PEFF file can hold one (\PName= reads
back as ""). prefix, db_unique_id and sequence are required.
header is only read for its :class:CustomKeyDef declarations, matched to each
record by prefix. It is needed whenever a record has custom_values, because
only a declared key is read back as a custom value rather than an unknown one, and
passing it is what keeps those values from being silently dropped. The entries are
returned on their own, as :func:to_records takes them, so the caller keeps owning
the header and passes it to :func:write_peff unchanged.
ncbi_tax_id, length, sv, ev and pe accept ints, integral floats
(9606.0, from a frame column with gaps) and digit strings. decoy accepts
True, 0, 1 and "true"/"false". The annotation columns,
custom_values and extra are the PEFF value text :func:to_records emits, and
are used as given.
Every entry is parsed from the description line that :func:write_peff would have
written, then turned back into a record and compared with the one that came in, so a
column that could not be honoured raises instead of changing the data.
Example
from pefftacular import SequenceEntry, from_records, to_records entry = SequenceEntry(prefix="sp", db_unique_id="P1", sequence="ACDE", pname="Alpha") record = to_records([entry])[0] record.get("pname"), record.get("variant_simple") ('Alpha', None) from_records([record]) == [entry] True
Raises:
| Type | Description |
|---|---|
PeffWriteError
|
A missing or unknown key, a bad value, a key set twice, a
|
Source code in src/pefftacular/_records.py
Compression¶
Compression types the compression argument of read_peff, PeffReader, write_peff
and to_records.
pefftacular.Compression
module-attribute
¶
How a PEFF file is (de)compressed, like the compression argument of pandas.
"infer"(the default): readers detect gzip, bzip2 or xz from the magic bytes; writers compress by the path suffix (.gz,.bz2,.xz, any case). An open handle is read and written as plain text."gzip","bz2","xz": always that format, whatever the suffix or content.None: never compressed.