better-profanity-rs
better_profanity with a Rust core: the same
censored text for every input, about 2,100 times faster on a 10-word message and
10,000 to 12,000 times faster on texts of 100 to 700 words
(benchmarks).
better_profanity is downloaded about two million times a month, although its last release, 0.7.0,
dates from November 2020. It is slow: contains_profanity takes 9 ms for a 10-word message and
0.9 s for a 700-word article on an M1 Pro with CPython 3.13. The word list is a Python list of 916
objects, and each word of the text, alone and joined with the words after it, is compared with every
one of them by a method that tries the leetspeak variants letter by letter. better-profanity-rs
turns the word list into a trie whose edges are sets of characters (a, @, * and 4 for an
a) and runs the original scan, step for step, in Rust. Words in leetspeak, phrases split by
separators (f.u.c.k, H4nd j0b), whitelists, custom word lists and the quirks of the original
give the same output.
The Python package is better-profanity-rs; the Rust crate is better-profanity. This is an
independent project, not affiliated with the author of better_profanity.
Python
pip install better-profanity-rs
Change the import and keep the rest of your code:
from better_profanity_rs import profanity # was: from better_profanity import profanity
profanity.censor("That wh0re gave m3 a very good H4nd j0b, dude.")
# 'That **** gave m3 a very good ****, dude.'
profanity.contains_profanity("he is a m0th3rf*cker")
# True
profanity.load_censor_words(["happy", "jolly"], whitelist_words=["jolly"])
profanity.censor("Happy and jolly days", "-")
# '---- and jolly days'
Profanity, load_censor_words, load_censor_words_from_file, add_censor_words, censor
and contains_profanity take the same arguments as in better_profanity 0.7.0 and return the
same results or raise the same exceptions. The attributes CENSOR_WORDSET, CHARS_MAPPING,
MAX_NUMBER_COMBINATIONS and ALLOWED_CHARACTERS can be read and changed as before, and the
modules better_profanity, constants, utils and varying_string are those of 0.7.0.
Code you do not control
When a library imports better_profanity itself, call install() before importing it:
import better_profanity_rs
better_profanity_rs.install() # `import better_profanity` now returns better_profanity_rs
import my_web_app
install() covers better_profanity and its modules better_profanity, constants, utils
and varying_string. Modules that imported better_profanity before the call keep the
original.
Rust
[dependencies]
better-profanity = "0.1"
use better_profanity::Profanity;
let profanity = Profanity::new();
assert_eq!(
profanity.censor("That wh0re gave m3 a very good H@nD j0b.", "*"),
"That **** gave m3 a very good ****."
);
let mut custom = Profanity::with_words(["happy", "jolly"]);
custom.add_censor_words(["merry christmas"]);
assert!(custom.contains_profanity("Merry Christmas!"));
Profanity follows the Python class; hide_swear_words, WordSet and CharSet expose the
scan for other word sets, separator sets and lowercasing rules. The crate lowercases with Rust's
Unicode tables, which can differ from those of a given Python for characters added to Unicode
recently; the Python package always uses the running interpreter's str.lower().
Compatibility
The tests compare better-profanity-rs with better_profanity 0.7.0 and fail on any
difference in output or exception: the original's own test suite, hand-picked cases,
dictionary words in leetspeak inside texts and joined by separators, custom word lists and
whitelists, invalid arguments, every kind of attribute change listed below, subclasses,
pickling, threads and property-based tests, on CPython 3.9 to 3.14. A separate script compares
285,074 texts, among them the civil_comments test split and the Jigsaw toxic comments (50,532
of the texts are censored), and 28,183 of them again with a custom word list and whitelist, on
CPython 3.9, 3.11, 3.13 and 3.14: no output differs (see BENCHMARKS.md).
This includes the original's quirks. For example, add_censor_words does not raise
MAX_NUMBER_COMBINATIONS, and when it is 1, only every other word is joined to the next one.
After Profanity(["happy", "jolly"]).add_censor_words(["merry christmas"]), both packages
censor "m3rry christmas" and "a b m3rry christmas" but not "a m3rry christmas".
better_profanity's own code, bundled in the package, runs whenever the Rust core could
behave differently, so the output stays the same and only the speed-up is lost:
- the text or
censor_charis not an exactstr(astrsubclass, for example), or contains lone surrogates; CENSOR_WORDSETis not an exact list or tuple ofVaryingStringobjects and strings, or aCHARS_MAPPINGvalue used by a word has a variant that is not exactly one character (better_profanityalso accepts longer and empty variants);MAX_NUMBER_COMBINATIONSis not anint, or the call is made so close to the recursion limit that the original, which recursesMAX_NUMBER_COMBINATIONScalls deep, could reach it (within about twiceMAX_NUMBER_COMBINATIONS+ 20 frames);ALLOWED_CHARACTERSwas replaced by another set, or holds something other than strings;- a subclass or a patch replaces
_get_next_words,_update_next_words_indices,_get_start_index_of_next_word,_get_next_word_and_end_index,get_replacement_for_swear_word,any_next_words_form_swear_wordorVaryingString.__eq__, or turns one of the attributes into a property.
The original runs in the same stack frame as in better_profanity, so a RecursionError is
raised exactly where better_profanity raises it. Changes in place are seen. CENSOR_WORDSET is compiled again when its items change: each call
checks that the list still holds the same objects, which takes about 1 µs for the default
list. ALLOWED_CHARACTERS is a set subclass that notes its changes. Lowercasing uses the
interpreter's str.lower(), so, as with better_profanity, results follow the Unicode version
of the Python running them.
Known differences:
- The private attributes of a
VaryingString(_char_combos,_min_len,_max_len) are read when the word list is compiled; changing them later on an object already inCENSOR_WORDSETis not seen. ALLOWED_CHARACTERSis asetsubclass; changes made throughsetitself, as inset.add(ALLOWED_CHARACTERS, "-"), are not seen.- Near the recursion limit, frames are counted with
sys._getframe. CPython 3.11 and older also count nested C calls toward the limit; the check allows one per Python frame, so a call made deep inside C-level recursion (a recursive__repr__, for example) could still raise wherebetter_profanitywould not, or the reverse. On CPython 3.11, a call made within two frames of the limit raisesRecursionErrorasbetter_profanitydoes, but the message can name a different operation. - A compiled word list keeps the
VaryingStringobjects ofCENSOR_WORDSETalive until the list changes or theProfanityobject is deleted. An attribute added to one of them that refers back to theProfanityobject keeps that object alive. - Free-threaded CPython builds are not supported: the extension module asks to keep the GIL enabled.
__version__andnameare those ofbetter_profanity_rs.
Development
cargo test -p better-profanity
cd bindings/python
uv sync --group dev
uv run pytest
cd benchmarks && uv run --group bench python parity.py # all corpora
License
MIT, the same licence as better_profanity, whose code, word list and character list this
project includes. See NOTICE.
Metadata
Release files for better-profanity-rs 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 | Provenance | |
|---|---|---|---|---|
| better_profanity_rs-0.1.0.tar.gz | 134.5 kB | With provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| better_profanity_rs-0.1.0-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| better_profanity_rs-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ x86-64 | Details |
| better_profanity_rs-0.1.0-cp39-abi3-musllinux_1_2_aarch64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ ARM64 | Details |
| better_profanity_rs-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| better_profanity_rs-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| better_profanity_rs-0.1.0-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| better_profanity_rs-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 2.7 MB
Release files / better_profanity_rs-0.1.0.tar.gz
| Download URL | better_profanity_rs-0.1.0.tar.gz |
|---|---|
| Size | 134.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7e81511de0836b12a720078be9e489e1ac40f046eaf421a775d28b91a8102901
|
|
BLAKE2b-256 checksum How to use checksums |
a208f43482f59364806b60a5448cd5e51128c7c344b17317af5a550d27b6a3d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-win_amd64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 208.4 kB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
974b420b6817b2ae714e529e0840d42cb59e6ab3060581218fa74dd3d95cf61b
|
|
BLAKE2b-256 checksum How to use checksums |
676090134aaa051883d818651658144a113b2d0989ad98b4c4c6ac845e30a9eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 541.4 kB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d0c5992fcb56687d33d5130f5d8f188a93184c8d5b77e6ffeaaa776ab433d0c1
|
|
BLAKE2b-256 checksum How to use checksums |
42fcd101e86fe081c3054759c585315c178dc5b7d29e00378a4a200394fec1cf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-musllinux_1_2_aarch64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 507.7 kB |
| Tags | CPython 3.9 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
613e9a471e0065cd4a26f9e0da2ac5400879767b85e21560b8036b8f35c94349
|
|
BLAKE2b-256 checksum How to use checksums |
762037f0f9815c36da05b84b27bb9e41354363234aa74ee8f353a97f856d2c4c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 329.2 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
563edea6e401d0119a5e66c366074d2bfae5836484d151e927347a514e628707
|
|
BLAKE2b-256 checksum How to use checksums |
3551ae16cbeeb7345b894d57595a98aadc1eb58d6c0f0a3090b69bc7267d540d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 329.3 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
149e2f87c1bac320cd72af8e7d2ab2130026f81c4dce1dc822b0e10e71701c9e
|
|
BLAKE2b-256 checksum How to use checksums |
96e5521d3e2d92f69291d7a7958a463afdaaea7e79c1c7ff3772cb8c3e281ef3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 301.5 kB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9928da2065a6393132f8fc0770940a84caf0c33d33bc55dcfdd2cd226c748012
|
|
BLAKE2b-256 checksum How to use checksums |
904e557ff18903e65403640d4bdf356ab9a502e83e0d01e5246306a1106eda97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logRelease files / better_profanity_rs-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | better_profanity_rs-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 305.1 kB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
8e8e1bf78ac640f47b861ef35d328f75fc7ec30769aaf34e9639f039ad4bf01a
|
|
BLAKE2b-256 checksum How to use checksums |
ca098fd640bc5d8f0c8397e1cbdfa481cab9984885115aa79af18fe2c42943ab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 21, 2026.
Transparency logSecurity for better-profanity-rs 0.1.0
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.
Full provenance
All 8 files in this release have been uploaded with provenance metadata.
Attestation for better_profanity_rs-0.1.0.tar.gz
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 Oct 21, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.