Skip to main content

A Polars expression plugin that validates peptide sequences at Rust speed

Project description

polars-is-peptide

PyPI Python CI Licence: MIT

A Polars expression plugin that answers one question quickly: is this string a valid peptide sequence?

A string is a peptide when every character is one of the 20 canonical amino acids — no spaces, no ambiguity codes, no punctuation, nothing else. The check is a byte scan in Rust, so it runs at about 25M rows/s: roughly 9× a per-row Python regex loop and 2× Polars' own regex engine (benchmark).

import polars as pl
from polars_is_peptide import is_peptide

df = pl.DataFrame({"seq": ["MKWVTFISLL", "MKW VTF", "MKWVTFISLLX", "mkwvtfisll", None]})

df.with_columns(valid=is_peptide("seq"))
┌─────────────┬───────┐
│ seq         ┆ valid │
│ ---         ┆ ---   │
│ str         ┆ bool  │
╞═════════════╪═══════╡
│ MKWVTFISLL  ┆ true  │   all canonical
│ MKW VTF     ┆ false │   space
│ MKWVTFISLLX ┆ false │   X is an ambiguity code, not an amino acid
│ mkwvtfisll  ┆ false │   lowercase is opt-in
│ null        ┆ null  │   missing is unknown, not invalid
└─────────────┴───────┘

Install

uv add polars-is-peptide
# or: pip install polars-is-peptide

Wheels are published for Linux (x86_64, aarch64), macOS (x86_64, arm64) and Windows (x64), so there's nothing to compile — you don't need a Rust toolchain to use this.

Requires Python ≥ 3.14 and Polars ≥ 1.42. The wheels are abi3-py314: one wheel per platform covers 3.14 and every later 3.x. If you're on an older Python, this release won't install; open an issue if you need a 3.9+ build and I'll widen the ABI floor.

To build from source instead, you'll need a Rust toolchain and uv:

git clone https://github.com/drchristhorpe/polars-is-peptide
cd polars-is-peptide
uv sync            # fetches Python 3.14, builds the Rust extension, installs it
uv run pytest      # 46 tests

uv sync installs a uv-managed standalone Python 3.14 (pinned in .python-version) into uv's own cache. Your system Python is not touched.

Usage

is_peptide takes a column name or an expression, and returns a boolean expression. Use it anywhere an expression goes — select, with_columns, filter, lazy or eager:

df.filter(is_peptide("seq"))                     # keep only the valid rows
df.with_columns(valid=is_peptide(pl.col("seq"))) # flag them instead
lf.filter(is_peptide("seq")).collect()           # works lazily too

There is also a .peptide namespace, if you prefer method chaining:

df.with_columns(valid=pl.col("seq").peptide.is_valid())

The alphabet

By default, only these are valid — the 20 canonical amino acids, uppercase:

A C D E F G H I K L M N P Q R S T V W Y

Four keyword arguments widen that. Each is off by default and additive, so the default behaviour can never silently start accepting something it didn't before:

Argument Default Effect
allow_lowercase False Accept lowercase residues too ("acdef"). Useful for soft-masked FASTA.
allow_ambiguous False Additionally accept B (Asx), J (Leu/Ile), X (any), Z (Glx).
allow_extended False Additionally accept O (pyrrolysine) and U (selenocysteine).
min_length 1 Sequences shorter than this are invalid. 0 accepts "".
df.with_columns(
    strict=is_peptide("seq"),
    permissive=is_peptide("seq", allow_lowercase=True, allow_ambiguous=True),
    real_peptides=is_peptide("seq", min_length=5),
)

The alphabets are exported, so you don't have to retype them:

from polars_is_peptide import AMINO_ACIDS, AMBIGUOUS_CODES, EXTENDED_RESIDUES

Two behaviours worth knowing

Nulls stay null. They do not become false. A missing sequence is unknown, not invalid, and the difference matters at the point you filter:

df.filter(is_peptide("seq"))    # drops null rows (null is not true)
df.filter(~is_peptide("seq"))   # ALSO drops them (NOT null is null)

A null row falls through both filters. That's deliberate — you should decide what a missing sequence means for your pipeline, rather than have the plugin quietly file it under "invalid". If you do want nulls treated as invalid, say so explicitly:

df.filter(~is_peptide("seq").fill_null(False))   # rejected, nulls included

The empty string is false, because a peptide has at least one residue. Pass min_length=0 for the vacuous-truth reading.

Non-ASCII input is rejected rather than raising: validation is a byte scan against a 256-entry table with no entry set at or above 0x80, so no byte of a multi-byte character can be mistaken for a residue.

Performance

1,000,000 sequences averaging 50 residues (50.4M residues total), on this machine:

Implementation Time Throughput
polars-is-peptide 0.039 s 25.6M rows/s
Polars native str.contains regex 0.091 s 11.0M rows/s 2.3× slower
Python re.match per row 0.341 s 2.9M rows/s 8.7× slower
Python set-membership per row 1.366 s 0.7M rows/s 34.9× slower

All four agree on all million rows. Reproduce with uv run python tmp/demo.py.

The speed comes from compiling the options into a [bool; 256] lookup table once per call, which turns per-character validation into a single indexed load, then scanning each row's bytes with early exit on the first invalid one. No allocation per row, and no decoding — the table's shape is what makes the byte scan UTF-8 safe.

Development

uv sync                      # build the Rust extension + install dev deps
uv run pytest                # test
uv run maturin develop -r    # rebuild in release mode after editing Rust
cargo check --lib            # fast Rust-only feedback loop

Layout: the Rust kernel is src/alphabet.rs (the lookup table) and src/expressions.rs (the Polars expression); the Python API is python/polars_is_peptide/init.py. tmp/demo.py is a runnable walkthrough, and PLAN.md records the design decisions and why they went the way they did.

One pinning note, because it will bite whoever upgrades next: pyo3 is held at 0.28 because that's what pyo3-polars 0.27 links against. Bumping pyo3 on its own fails the build with a links = "python" conflict. Bump the two together, or neither.

Releasing

Wheels are built and published by CI on a version tag, using PyPI Trusted Publishing — there is no API token stored in the repo or in GitHub secrets.

# bump the version in pyproject.toml AND Cargo.toml (they must match),
# add a CHANGELOG entry, commit and push, then:
git tag v0.1.1 && git push origin v0.1.1

Land every doc change before you tag. The tag is what CI builds, and the README is baked into the wheel metadata at build time — that's what PyPI renders on the project page. A README fixed after tagging will not appear on PyPI, and neither the files nor the metadata of a published version can be replaced; the only remedy is another release. This caught us on 0.1.0.

That builds Linux (x86_64, aarch64), macOS (x86_64, arm64) and Windows (x64) wheels plus an sdist, runs the test suite against each built wheel, and publishes to PyPI. The matrix is deliberately narrower than maturin's default, which also emits armv7, s390x, ppc64le and 32-bit targets — Polars publishes wheels for none of those, so our own dependency wouldn't resolve there.

Because the wheels are abi3-py314, one wheel per platform covers 3.14 and every later 3.x. Nothing needs rebuilding when 3.15 lands.

Licence

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polars_is_peptide-0.1.1.tar.gz (39.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

polars_is_peptide-0.1.1-cp314-abi3-win_amd64.whl (4.2 MB view details)

Uploaded CPython 3.14+Windows x86-64

polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.14+manylinux: glibc 2.17+ x86-64

polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.2 MB view details)

Uploaded CPython 3.14+manylinux: glibc 2.17+ ARM64

polars_is_peptide-0.1.1-cp314-abi3-macosx_11_0_arm64.whl (3.2 MB view details)

Uploaded CPython 3.14+macOS 11.0+ ARM64

polars_is_peptide-0.1.1-cp314-abi3-macosx_10_12_x86_64.whl (3.6 MB view details)

Uploaded CPython 3.14+macOS 10.12+ x86-64

File details

Details for the file polars_is_peptide-0.1.1.tar.gz.

File metadata

  • Download URL: polars_is_peptide-0.1.1.tar.gz
  • Upload date:
  • Size: 39.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for polars_is_peptide-0.1.1.tar.gz
Algorithm Hash digest
SHA256 5fc5bd238bce2b8cf44337a169da9aa1e3f17b21035dc5378b9dd61e3832e856
MD5 0d47441caf32a27e0203dbeb0fb11bfc
BLAKE2b-256 edb176213d83617c841af7cd3259ee6fed38cc4889e2b43c4b308011b7746ab9

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_is_peptide-0.1.1.tar.gz:

Publisher: CI.yml on drchristhorpe/polars-is-peptide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polars_is_peptide-0.1.1-cp314-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for polars_is_peptide-0.1.1-cp314-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 3ecef6a5e5853d1ba057fdbfbfa4d492eec3ae79898500579faf44859c8cf896
MD5 016ccd3c89d35098d010f17a6e07d1cd
BLAKE2b-256 d839904ec021312bb3dfcd6c79cd7399d392770456ba240480df3da2de43fe92

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_is_peptide-0.1.1-cp314-abi3-win_amd64.whl:

Publisher: CI.yml on drchristhorpe/polars-is-peptide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 eaa10ee89a119dacac06ed4c6c426ed4c27b8bc241103c1521fb453cd503e269
MD5 eb321b83b3ef5a5680effcd3e1ce0321
BLAKE2b-256 fd4b27c55ac70b186a1be0dc22f50290881f069939ed24f01489245bdb08e77c

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: CI.yml on drchristhorpe/polars-is-peptide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e3d97d599b23ea04fb48c983ee2c15279b0a2ede87ec4e70e47d9a8fc0e71964
MD5 0e7cd194a40046450b8bc4e14bff74cc
BLAKE2b-256 12e2c6cf1591d08fe94fd8c6e8e41a7ebdc9c4adebf819e66ed55cfbfb747caa

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_is_peptide-0.1.1-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: CI.yml on drchristhorpe/polars-is-peptide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polars_is_peptide-0.1.1-cp314-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for polars_is_peptide-0.1.1-cp314-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 31399d42136b47c2883d12326ad23065fda0ce8df0d73d0e3195d59e9b9e029d
MD5 d868fe9f48da8184a955e452cd1ab2a5
BLAKE2b-256 33bcefbaf05067bd142ce4e761eade624626e2df718962b69ae6e91887b8c07e

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_is_peptide-0.1.1-cp314-abi3-macosx_11_0_arm64.whl:

Publisher: CI.yml on drchristhorpe/polars-is-peptide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polars_is_peptide-0.1.1-cp314-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for polars_is_peptide-0.1.1-cp314-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 b119c4eb288bc5f9f1f7db87b6b75164275383ea499f0352b5f011dad0649683
MD5 d17872529558d3e22dfda4824e60f15f
BLAKE2b-256 3db0cc539700b91f504403ace2522ec12806a426d522c16726e632ae2d42d9c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_is_peptide-0.1.1-cp314-abi3-macosx_10_12_x86_64.whl:

Publisher: CI.yml on drchristhorpe/polars-is-peptide

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page