Chemur
Find the non-covalent interactions in a PDB or mmCIF structure — between proteins, nucleic acids, ligands, metals and solvent, in any combination.
pip install chemur
chemur analyze structure.cif --out interactions.json
- 22 interaction types, each with defined geometry
- Any pair of components. Protein–ligand, protein–protein, protein–DNA/RNA, nucleic acid–ligand, ligand–ligand. Nothing is hard-coded to expect a receptor and a small molecule.
- Chemistry from templates, geometry from coordinates. Ligands are typed from SMILES, an SDF, or an automatic Chemical Component Dictionary lookup.
- MD trajectories — per-frame analysis with occupancy and time series.
A ChimeraX plugin builds on this library for interactive 3D visualisation.
Install
pip install chemur
Wheels are published for CPython 3.10–3.13 on Linux x86_64, macOS (a single universal2 build covering Intel and Apple Silicon) and Windows x86_64. Other platforms build from the source distribution and need a C++17 compiler and CMake ≥ 3.18.
| Extra | Installs | Enables |
|---|---|---|
trajectory |
MDAnalysis |
reading trajectory files |
dataframe |
pandas |
AnalysisResult.to_dataframe() |
all |
both |
pip install "chemur[all]"
pH-range ligand protonation (--protonate) needs no extra — dimorphite_dl is a hard
dependency. It requires rdkit<2026, so a Chemur install is capped there too.
MDAnalysisis LGPL-3.0-or-later (it relicensed from GPL at 2.8.0; the>=2.4floor still permits older GPL-licensed releases if you pin one). Chemur itself is MIT and neither bundles nor links it, but installing thetrajectoryextra adds copyleft-licensed code to your environment.
To confirm you got the compiled core rather than the fallback:
python -c "from chemur.core import USING_CPP_CORE; print('C++ core:', USING_CPP_CORE)"
True is expected. False means the pure-Python fallback loaded — correct results,
10–100× slower. From a wheel that should never happen; from a source build it means
CMake or the compiler failed. Set CHEMUR_FORCE_PYTHON_CORE=1 to select it deliberately.
Quick start
import chemur
result = chemur.analyze("structure.cif")
for interaction in result.interactions:
print(interaction.interaction_type, interaction.component_ids)
result.to_json("interactions.json")
chemur analyze structure.cif --out interactions.json --csv interactions.csv
Any biomolecule, not just protein–ligand
analyze returns every interaction it finds in the structure. There is no receptor
or ligand role — you pick out the ones you want by component.
import chemur
from collections import Counter
result = chemur.analyze("complex.cif")
# What was found, and between what?
print(Counter(i.interaction_type for i in result.interactions))
A protein–DNA interface, a protein–protein interface and a ligand binding site are all the same query — filter on the components involved:
def between(result, a, b):
"""Interactions with one atom in component `a` and the other in `b`."""
return [
i for i in result.interactions
if {a, b} <= set(i.component_ids)
]
interface = between(result, "A:ARG:145", "B:DG:12") # protein side chain to a base
Components are identified by component_id; result.components lists them all with
their names, so you can select a chain, a residue range, or a single ligand. Nucleic
acid phosphate backbones are perceived as anions, protein Arg/Lys as cations and
Asp/Glu as carboxylate anions, so salt bridges and cation–π across a protein–nucleic
acid interface are detected the same way as in a binding site.
What it detects
| Family | Types |
|---|---|
| Hydrogen bonding | hbond, weak_hbond, hbond_pi, solvent_bridge, amide_bridge |
| Electrostatic | salt_bridge, metal_coordination, cation_pi, anion_pi, anion_aromatic_edge |
| Stacking | pipi_stack, aliphatic_pi_stack, aliphatic_stack, amide_pi, ch_pi |
| σ-hole | halogen_bond, chalcogen_bond, tetrel_bond, halogen_pi, chalcogen_pi |
| Other | n_pi_star, hydrophobic |
aliphatic_stack (saturated–saturated ring stacking) ships disabled; enable it in the
profile. The σ-hole, stacking and n→π* geometries follow
Adhav & Saikrishnan, ACS Omega 2023, 8, 22268.
Each type has its own geometric criteria, all adjustable — see docs/cli.md for overriding cutoffs and docs/chemistry.md for the chemical gates (such as why an ordinary Met sulfur cannot donate a chalcogen bond).
Ligand chemistry
Ligands need a chemical template so protonation, charge, aromaticity and donor/acceptor status are correct. Chemur takes one from, in order of precedence:
chemur analyze structure.cif --ligand-smiles LIG='CC(=O)N' # explicit SMILES
chemur analyze receptor.pdb --ligand-sdf docked.sdf # an SDF (coordinates too)
chemur analyze structure.cif # automatic CCD lookup
The automatic lookup resolves each ligand by its component name against the RCSB
Chemical Component Dictionary. Add --protonate to set the protonation state for a pH
range with Dimorphite-DL, and --debug to print the SMILES actually used.
Full detail in docs/cli.md.
Multiple ligands and docking output
Pass several ligands at once, or point at a directory of sdf files.
chemur analyze receptor.pdb --ligand-sdf a.sdf --ligand-sdf b.sdf # repeatable
chemur analyze receptor.pdb --ligand-sdf-dir poses/ # a whole directory
By default all supplied ligands are added to the structure and analysed together, as one system — right for a cofactor plus a substrate, or two ligands sharing a pocket.
Docked poses are the opposite case: each is an alternative for the same site, so
analysing them together would stack overlapping copies into one pocket. Use --batch to
analyse each ligand independently against the same receptor:
chemur analyze receptor.pdb --ligand-sdf-dir poses/ --batch --out all_poses.json
Either way you also get one filtered file per ligand under ligand_outputs/ — in batch
mode under a subdirectory per pose — while --out / --csv write the aggregate.
SDF coordinates are used directly, and ligand residues already present in the structure are skipped so the SDF stays authoritative.
Trajectories
pip install "chemur[trajectory]"
chemur trajectory topology.pdb trajectory.xtc --out frames.json
Analyses each frame and reports per-interaction occupancy and time series.
Documentation
- docs/cli.md — full command-line reference
- docs/chemistry.md — templates, hydrogen policy, σ-hole activation, profiles
Development
git clone https://github.com/SweeneyAaron/chemur
cd chemur
conda create -n chemur-dev -c conda-forge \
python=3.11 rdkit gemmi numpy pyyaml pandas pytest \
cmake ninja pybind11 scikit-build-core cxx-compiler
conda activate chemur-dev
pip install -e ".[test,all]" -v
pytest
On macOS you need the Apple command line tools (xcode-select --install). If the build
fails with 'cstddef' file not found they are incomplete — repair with
sudo rm -rf /Library/Developer/CommandLineTools && xcode-select --install.
This project uses a src/ layout and has no conftest.py on purpose, so pytest always
exercises the installed package rather than the source tree. That is what makes the
wheel-level checks in CI meaningful.
License
MIT — see LICENSE.
Citation
A manuscript is in preparation. Until then, please cite the repository:
Sweeney, A, Genz, L, Topf, M. Chemur: biomolecular interaction detection.
https://github.com/SweeneyAaron/chemur
Metadata
Release files for chemur 0.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 | |
|---|---|---|---|
| chemur-0.1.0.tar.gz | 120.4 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| chemur-0.1.0-cp313-cp313-win_amd64.whl | CPython 3.13 | CPython 3.13 | Windows x86-64 | Details |
| chemur-0.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.28+ x86-64, Linux glibc 2.24+ x86-64 | Details |
| chemur-0.1.0-cp313-cp313-macosx_11_0_universal2.whl | CPython 3.13 | CPython 3.13 | macOS 11.0+ universal2 (ARM64, x86-64) | Details |
| chemur-0.1.0-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details |
| chemur-0.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.28+ x86-64, Linux glibc 2.24+ x86-64 | Details |
| chemur-0.1.0-cp312-cp312-macosx_11_0_universal2.whl | CPython 3.12 | CPython 3.12 | macOS 11.0+ universal2 (ARM64, x86-64) | Details |
| chemur-0.1.0-cp311-cp311-win_amd64.whl | CPython 3.11 | CPython 3.11 | Windows x86-64 | Details |
| chemur-0.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.24+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| chemur-0.1.0-cp311-cp311-macosx_11_0_universal2.whl | CPython 3.11 | CPython 3.11 | macOS 11.0+ universal2 (ARM64, x86-64) | Details |
| chemur-0.1.0-cp310-cp310-win_amd64.whl | CPython 3.10 | CPython 3.10 | Windows x86-64 | Details |
| chemur-0.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.10 | CPython 3.10 | Linux glibc 2.24+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| chemur-0.1.0-cp310-cp310-macosx_11_0_universal2.whl | CPython 3.10 | CPython 3.10 | macOS 11.0+ universal2 (ARM64, x86-64) | Details |
Total release size: 2.6 MB
Release files / chemur-0.1.0.tar.gz
| Download URL | chemur-0.1.0.tar.gz |
|---|---|
| Size | 120.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0e7ed73ca3f3543b54b5e3ad081f98f2db083bea55d3cc7ebd79c38c7aa8c35
|
|
BLAKE2b-256 checksum How to use checksums |
ccea3a4f6afde3e34b73ddf41a5ab60954ef284a6654c0dc1c835c1f9e40882c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp313-cp313-win_amd64.whl
| Download URL | chemur-0.1.0-cp313-cp313-win_amd64.whl |
|---|---|
| Size | 191.7 kB |
| Tags | CPython 3.13 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
a5189a8600a4c0dc81092626744ebbbd8ab76a86f562197db161e4894d3f54b8
|
|
BLAKE2b-256 checksum How to use checksums |
11233ae89658bffd8847b147376b140b3ea1e615fe1022312f33643ce972ea36
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | chemur-0.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 187.7 kB |
| Tags | CPython 3.13 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
85e156f0ae429a3061e631f92ae380561cb47b7192fbb137f530ad774de4fcf3
|
|
BLAKE2b-256 checksum How to use checksums |
f761bbd5017f1ffb1026522d00e49c22c28c8fc94edf44b85d230833863868c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp313-cp313-macosx_11_0_universal2.whl
| Download URL | chemur-0.1.0-cp313-cp313-macosx_11_0_universal2.whl |
|---|---|
| Size | 239.9 kB |
| Tags | CPython 3.13 macOS 11.0+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
0c25bbcd79f3f712f95bcf08d21b5f749d0e51db33872114334e12bbd3c9907e
|
|
BLAKE2b-256 checksum How to use checksums |
ae2a4342bc562181f3ac776c218edc3f469800c592d08f284f9709811ad46fc6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp312-cp312-win_amd64.whl
| Download URL | chemur-0.1.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 191.7 kB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
8d419aad4585cca72b6d472017598e42f16333b8c8867df8cc33aa2f8a502846
|
|
BLAKE2b-256 checksum How to use checksums |
ce7c485d4ab0051d21be9531badc2d632e1e07b588275c7039995d7a35fc1733
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | chemur-0.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 187.7 kB |
| Tags | CPython 3.12 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
760c7a4317c981b5c741bece53101468adf6ed985b7d6793c39f5d53e480da5a
|
|
BLAKE2b-256 checksum How to use checksums |
72db0d557911ebb393052f8de034081c9e4cea7aedc8629783ec9ec657481cf0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp312-cp312-macosx_11_0_universal2.whl
| Download URL | chemur-0.1.0-cp312-cp312-macosx_11_0_universal2.whl |
|---|---|
| Size | 239.8 kB |
| Tags | CPython 3.12 macOS 11.0+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
075ef0f4ccc261724700bc75b7e8205eed7c2868d9d8f76f0819be5c88ca7fe3
|
|
BLAKE2b-256 checksum How to use checksums |
976370fc70bb53f63dc99099777acf9b3cead103d16e4b6d80095e6acf667754
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp311-cp311-win_amd64.whl
| Download URL | chemur-0.1.0-cp311-cp311-win_amd64.whl |
|---|---|
| Size | 190.1 kB |
| Tags | CPython 3.11 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
e438e582f0da09b7137b7734edd53bad9945d3de11769a56a2f2d2a032ef403d
|
|
BLAKE2b-256 checksum How to use checksums |
b38eb0244e7c8826e34c58e52cae554bd88e71e9828bcfd6196d9b6c5a7b6abe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | chemur-0.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 187.6 kB |
| Tags | CPython 3.11 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
9fa1d5e0a7052fc392127e0cf7a33eb87032739a069ab8b137e0b2f10e0ed3f8
|
|
BLAKE2b-256 checksum How to use checksums |
00fb8c1ab29ff1bf0b1b14515cd7582db41cae1b23dc47cad419281bc503a890
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp311-cp311-macosx_11_0_universal2.whl
| Download URL | chemur-0.1.0-cp311-cp311-macosx_11_0_universal2.whl |
|---|---|
| Size | 238.6 kB |
| Tags | CPython 3.11 macOS 11.0+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
be8cd0221403d25624eab71fcd0aab193d4d153dbd4d5b5f63832d3d6d6d843c
|
|
BLAKE2b-256 checksum How to use checksums |
e9c648b681c6fa8b9b0da98583c4dd0e368f2fcd7d90946081a52b686f4e4325
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp310-cp310-win_amd64.whl
| Download URL | chemur-0.1.0-cp310-cp310-win_amd64.whl |
|---|---|
| Size | 189.4 kB |
| Tags | CPython 3.10 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
8064e6881768c628a1a6951d3c90ff2f15432e11127976ffbf737e34529d2fd5
|
|
BLAKE2b-256 checksum How to use checksums |
f5a9eed3a397d9814242c72f2ae6d64765ba23d98a1474d92f94c4691992428a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | chemur-0.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 186.1 kB |
| Tags | CPython 3.10 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
152c84b567ba41a8bd8c2ed187e81da68b6f04378fcb71992e1e60c1fb959cff
|
|
BLAKE2b-256 checksum How to use checksums |
6deb34f5bb24fd061e1b599b451ad21457ab29f9e9c1c5fe8ac0b6b1dce6680f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency logRelease files / chemur-0.1.0-cp310-cp310-macosx_11_0_universal2.whl
| Download URL | chemur-0.1.0-cp310-cp310-macosx_11_0_universal2.whl |
|---|---|
| Size | 235.7 kB |
| Tags | CPython 3.10 macOS 11.0+ universal2 (ARM64, x86-64) |
|
SHA-256 checksum How to use checksums |
9db96c2a71efee080cb8119860e25cc769ed7cd0b2615a99e38893761cbc65a0
|
|
BLAKE2b-256 checksum How to use checksums |
1f93b8e7ac5301ebf03c3871373f08cb9c15cd07779be1db7cd28247bf077c57
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 30, 2026.
Transparency log