A Polars expression plugin that validates peptide sequences at Rust speed
Project description
polars-is-peptide
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
Not on PyPI yet — build it from source. You'll need a Rust toolchain, plus uv; Python 3.14 and Polars come along for the ride.
git clone <this repo> && 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, add a CHANGELOG entry, then:
git tag v0.1.0 && git push origin v0.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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
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 polars_is_peptide-0.1.0.tar.gz.
File metadata
- Download URL: polars_is_peptide-0.1.0.tar.gz
- Upload date:
- Size: 38.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f53bae6ae4bb63428fb635f2b3c326c37b79b3c8057dc38af15aadc751552d48
|
|
| MD5 |
28796b9b2695f33c76a75a847bd6c4a6
|
|
| BLAKE2b-256 |
06452b7ec58db38ac4d45bb28c773382ba296babd0f5c22ff8d544e1e6dff0fa
|
Provenance
The following attestation bundles were made for polars_is_peptide-0.1.0.tar.gz:
Publisher:
CI.yml on drchristhorpe/polars-is-peptide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_is_peptide-0.1.0.tar.gz -
Subject digest:
f53bae6ae4bb63428fb635f2b3c326c37b79b3c8057dc38af15aadc751552d48 - Sigstore transparency entry: 2170302837
- Sigstore integration time:
-
Permalink:
drchristhorpe/polars-is-peptide@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/drchristhorpe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
CI.yml@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_is_peptide-0.1.0-cp314-abi3-win_amd64.whl.
File metadata
- Download URL: polars_is_peptide-0.1.0-cp314-abi3-win_amd64.whl
- Upload date:
- Size: 4.2 MB
- Tags: CPython 3.14+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0353c75fe6a518c6f343dc6d6841f149e6397d13d5bea6f1a2ad47245eb93679
|
|
| MD5 |
434ed9bc7f5af312e0c7c71a3ac989c5
|
|
| BLAKE2b-256 |
c45fb111d5fc327929e9de86834e0b381412438adf91c0fee09bb0652f5dc829
|
Provenance
The following attestation bundles were made for polars_is_peptide-0.1.0-cp314-abi3-win_amd64.whl:
Publisher:
CI.yml on drchristhorpe/polars-is-peptide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_is_peptide-0.1.0-cp314-abi3-win_amd64.whl -
Subject digest:
0353c75fe6a518c6f343dc6d6841f149e6397d13d5bea6f1a2ad47245eb93679 - Sigstore transparency entry: 2170302932
- Sigstore integration time:
-
Permalink:
drchristhorpe/polars-is-peptide@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/drchristhorpe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
CI.yml@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.6 MB
- Tags: CPython 3.14+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
488ff25a70e6d20fe12f623eedfdb3314ac93b5436718118bd81f7853edcd9fc
|
|
| MD5 |
177bbef00170b3cf7cb5be5d94551f55
|
|
| BLAKE2b-256 |
f53d254fc1d39c9c8d999e9cf116e33542092b10ee48c54eb9ad5797829dd791
|
Provenance
The following attestation bundles were made for polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
CI.yml on drchristhorpe/polars-is-peptide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
488ff25a70e6d20fe12f623eedfdb3314ac93b5436718118bd81f7853edcd9fc - Sigstore transparency entry: 2170303109
- Sigstore integration time:
-
Permalink:
drchristhorpe/polars-is-peptide@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/drchristhorpe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
CI.yml@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.2 MB
- Tags: CPython 3.14+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
505a3c74e4e21e25fe4f43616cabdf2c025e4784e4584638499458abbd25fd25
|
|
| MD5 |
33e287efe8773d1992b877cb3c676d84
|
|
| BLAKE2b-256 |
b791cdf1ab8ec3efe7f9a02cf45a60efcf9bf23c5e4494199c2860afd3032081
|
Provenance
The following attestation bundles were made for polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
CI.yml on drchristhorpe/polars-is-peptide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_is_peptide-0.1.0-cp314-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
505a3c74e4e21e25fe4f43616cabdf2c025e4784e4584638499458abbd25fd25 - Sigstore transparency entry: 2170303892
- Sigstore integration time:
-
Permalink:
drchristhorpe/polars-is-peptide@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/drchristhorpe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
CI.yml@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_is_peptide-0.1.0-cp314-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: polars_is_peptide-0.1.0-cp314-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.2 MB
- Tags: CPython 3.14+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
136e51491f0615cd5c985c25c49c41c18d5322ba481828d2e9ee3a42cc3ee9d7
|
|
| MD5 |
6943209fae2b4943eb3110c56b46c924
|
|
| BLAKE2b-256 |
f064edf4f17e61a4daae66e31372a4c152dfcfc91a9fa98a04feaed472528844
|
Provenance
The following attestation bundles were made for polars_is_peptide-0.1.0-cp314-abi3-macosx_11_0_arm64.whl:
Publisher:
CI.yml on drchristhorpe/polars-is-peptide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_is_peptide-0.1.0-cp314-abi3-macosx_11_0_arm64.whl -
Subject digest:
136e51491f0615cd5c985c25c49c41c18d5322ba481828d2e9ee3a42cc3ee9d7 - Sigstore transparency entry: 2170304140
- Sigstore integration time:
-
Permalink:
drchristhorpe/polars-is-peptide@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/drchristhorpe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
CI.yml@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_is_peptide-0.1.0-cp314-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: polars_is_peptide-0.1.0-cp314-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.6 MB
- Tags: CPython 3.14+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25388ec0e2f47fde380293a9631473d94f965fb4bab31acbc798b86a0ae5c6b8
|
|
| MD5 |
905c9411a4e088e6e59c26ad0077fb1d
|
|
| BLAKE2b-256 |
2589e7a46b031a184acd44852539a52e4343f5244aa76864b71615a96bf846fe
|
Provenance
The following attestation bundles were made for polars_is_peptide-0.1.0-cp314-abi3-macosx_10_12_x86_64.whl:
Publisher:
CI.yml on drchristhorpe/polars-is-peptide
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_is_peptide-0.1.0-cp314-abi3-macosx_10_12_x86_64.whl -
Subject digest:
25388ec0e2f47fde380293a9631473d94f965fb4bab31acbc798b86a0ae5c6b8 - Sigstore transparency entry: 2170304435
- Sigstore integration time:
-
Permalink:
drchristhorpe/polars-is-peptide@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/drchristhorpe
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
CI.yml@085c15fd10dda9cef1441631808fbb5eb6735de6 -
Trigger Event:
push
-
Statement type: