safetype (Python)
Rule-based content moderation for Python servers: profanity, hate speech and harassment. It's the same engine and English dictionary as the SafeType.js JavaScript packages, so your backend can re-check exactly what the browser checked. It has no dependencies and runs fully offline.
pip install safetype
from safetype import create_moderation_engine
engine = create_moderation_engine(mode="mask")
engine.moderate("What the fuck are you doing?").text
# 'What the f*ck are you doing?'
Server-side enforcement
Client-side moderation can be bypassed (devtools, curl, modified clients). Re-check before you store:
engine = create_moderation_engine(
mode="mask",
policy={"hate": {"action": "block"}, "harassment": {"action": "block"}},
)
check = engine.validate(comment)
if not check.valid:
raise ValidationError([v.category for v in check.violations])
store(check.result.text) # profanity masked
if check.result.requires_review:
queue_for_human_review(comment) # e.g. quoted or reported speech
FastAPI:
@app.post("/comments")
def create_comment(body: CommentIn):
check = engine.validate(body.text)
if not check.valid:
raise HTTPException(422, detail=[{"category": v.category, "rule": v.rule_id} for v in check.violations])
return save(check.result.text)
Django form:
def clean_body(self):
check = engine.validate(self.cleaned_data["body"])
if not check.valid:
raise forms.ValidationError("Please keep it respectful.")
return check.result.text
What it handles
- Five actions (
allow,mask,suggest,warn,block) and per-category policies - Profanity is masked deterministically. Hate and harassment rules are lower-confidence signals that context can weaken:
quoted or reported speech becomes
requires_reviewinstead of being acted on - Unicode (accents, full-width, Cyrillic/Greek lookalikes, zero-width), leet (
sh1t), repeated letters, spaced letters - No Scunthorpe problem, and masking is idempotent
- Custom words, custom rules, allow lists, mask styles (
vowels,partial,full, or a function)
API
engine = create_moderation_engine(
mode="mask", language="en", categories=None, policy=None, packs=None,
custom_words=None, allow_list=None, rules=None,
mask_char="*", mask_style="vowels", detect_spaced_letters=True,
)
engine.moderate(text) # ModerationResult(text, suggested_text, matches, action, allowed, requires_review, mapping)
engine.mask(text) # str
engine.validate(text) # ValidationResult(valid, result, violations)
engine.add_rule({"id": "app.x", "pattern": "(foo|bar*)", "category": "custom"})
engine.remove_rule("app.x")
engine.set_policy({"harassment": {"action": "block", "min_confidence": 0.7}})
engine.set_mode("warn")
safetype.moderate(text); safetype.mask(text) # shared default engine
Offsets (match.start / match.end) are Python string indices (code points).
Parity with JavaScript
The test suite replays 684 cases recorded from the JavaScript engine and requires identical results. The dictionary is exported from the JavaScript build, so both always share the same rules.
Limitations
Rule-based detection misses novel spellings and sarcasm, and it can flag reclaimed or quoted language. Treat hate and harassment results as signals for review, not verdicts. Contextual (AI) classifiers are available in the JavaScript engine and planned for Python.
MIT licensed.
Metadata
Release files for safetype 1.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| safetype-1.5.1.tar.gz | 40.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| safetype-1.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.6 kB
Release files / safetype-1.5.1.tar.gz
| Download URL | safetype-1.5.1.tar.gz |
|---|---|
| Size | 40.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
34a4eb48a8794814206c445454d8131b1a0860f0e60e9101dd1585c1e6d8ae72
|
|
BLAKE2b-256 checksum How to use checksums |
ec955085ad824641378cf0006848fa337574356a1fcbf512cb6065cf82a32f92
|
| 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 Oct 14, 2026.
Transparency logRelease files / safetype-1.5.1-py3-none-any.whl
| Download URL | safetype-1.5.1-py3-none-any.whl |
|---|---|
| Size | 19.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
408c67f4cf2bd1b6b08f07d1add6f5886c28d3fe20993c7c3717f4ddaf0512cd
|
|
BLAKE2b-256 checksum How to use checksums |
d36a63c41e554df50f8cee12230a4698f1ae8ccf450149070a0c23d064b7c7c3
|
| 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 Oct 14, 2026.
Transparency log