Skip to main content

better-profanity-rs

CI PyPI Crates.io License

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_char is not an exact str (a str subclass, for example), or contains lone surrogates;
  • CENSOR_WORDSET is not an exact list or tuple of VaryingString objects and strings, or a CHARS_MAPPING value used by a word has a variant that is not exactly one character (better_profanity also accepts longer and empty variants);
  • MAX_NUMBER_COMBINATIONS is not an int, or the call is made so close to the recursion limit that the original, which recurses MAX_NUMBER_COMBINATIONS calls deep, could reach it (within about twice MAX_NUMBER_COMBINATIONS + 20 frames);
  • ALLOWED_CHARACTERS was 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_word or VaryingString.__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 in CENSOR_WORDSET is not seen.
  • ALLOWED_CHARACTERS is a set subclass; changes made through set itself, as in set.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 where better_profanity would not, or the reverse. On CPython 3.11, a call made within two frames of the limit raises RecursionError as better_profanity does, but the message can name a different operation.
  • A compiled word list keeps the VaryingString objects of CENSOR_WORDSET alive until the list changes or the Profanity object is deleted. An attribute added to one of them that refers back to the Profanity object keeps that object alive.
  • Free-threaded CPython builds are not supported: the extension module asks to keep the GIL enabled.
  • __version__ and name are those of better_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)

Source distribution for better-profanity-rs 0.1.0
File Size Uploaded Provenance
better_profanity_rs-0.1.0.tar.gz 134.5 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for better-profanity-rs 0.1.0
File
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 log

Release 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 log

Release 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 log

Release 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 log

Release 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 log

Release 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 log

Release 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 log

Release 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 log

Security 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.

Select from 8 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

8 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page