cleanmonkey
One-call text cleanup for invisible characters, smart quotes, and whitespace normalization.
Install
pip install cleanmonkey
Quick Start
from cleanmonkey import clean
# Sensible defaults handle the common garbage
clean("hello\u00a0world\u2019s \u2014 test")
# → "hello world's - test"
# Idempotent — safe to call twice
clean(clean(text)) == clean(text)
What It Cleans (by default)
| Category | Examples | Result |
|---|---|---|
| Non-breaking spaces | \u00a0, \u2007, \u202f |
Regular space |
| Zero-width chars | \u200b, \u200c, \u200d, \ufeff |
Removed |
| Smart quotes | \u2018 \u2019 \u201c \u201d |
' and " |
| Dashes | \u2013 (en), \u2014 (em) |
- |
| Ellipsis | \u2026 |
... |
| Control chars | null, form feed, vertical tab | Removed |
| Line endings | \r\n, \r |
\n |
| Multiple spaces | "hello world" |
"hello world" |
| Leading/trailing | " hello " |
"hello" |
Granular Control
Override any default:
clean(text, smart_quotes=False) # keep curly quotes
clean(text, dashes=False) # keep em/en dashes
clean(text, fullwidth=True) # also normalize fullwidth digits/letters
clean(text, collapse_spaces=False) # keep multiple spaces
clean(text, strip=False) # keep leading/trailing whitespace
Profiles
clean(text, profile="default") # all normalizations (the default)
clean(text, profile="csv") # default + fullwidth normalization
clean(text, profile="sql") # default + fullwidth normalization
clean(text, profile="display") # keep smart quotes & dashes; still clean invisible, control, whitespace, line endings
clean(text, profile="minimal") # invisible chars only, no collapsing or stripping
clean(text, profile="aggressive") # everything including fullwidth
Batch Helpers
from cleanmonkey import clean_column, clean_dict
# Clean a list (non-strings pass through)
clean_column(["hello\u00a0world", 42, None])
# → ["hello world", 42, None]
# Recursively clean dict values
clean_dict({"name": "John\u00a0Doe", "nested": {"val": "test\u200b"}})
# → {"name": "John Doe", "nested": {"val": "test"}}
# Also clean keys
clean_dict({"key\u00a0name": "val"}, keys=True)
# → {"key name": "val"}
Inspect
Find out what's lurking in your text:
from cleanmonkey import inspect
for info in inspect("hello\u00a0world\u200b"):
print(f"{info.codepoint} {info.name} count={info.count} at {info.positions}")
# U+00A0 NO-BREAK SPACE count=1 at [5]
# U+200B ZERO WIDTH SPACE count=1 at [11]
CLI
# Clean a file
cleanmonkey input.txt -o output.txt
# Pipe through stdin
cat dirty.csv | cleanmonkey > clean.csv
# Use a profile
cleanmonkey --profile csv input.txt
# Inspect mode — report what's in a file
cleanmonkey --inspect input.txt
# Machine-readable JSON inspect output
cleanmonkey --json input.txt
# Selective overrides
cleanmonkey --no-smart-quotes --fullwidth input.txt
# Preserve whitespace structure
cleanmonkey --no-strip --no-collapse-spaces input.txt
# Preserve line endings (CR/CRLF)
cleanmonkey --no-line-endings input.txt
Built for LLMs
cleanmonkey is designed to work well as a tool for large language models. Invisible character cleanup is a constant source of silent bugs in LLM-driven data pipelines — non-breaking spaces break splits, zero-width characters corrupt comparisons, and smart quotes fail exact matches. Without cleanmonkey, LLMs end up generating repetitive .replace() chains that miss edge cases and waste tokens. A single clean() call handles all of it with a structured, idempotent result — no multi-step prompting or character-by-character debugging required. Fewer tokens in, clean data out.
A note on whitespace
By default clean() strips each line and collapses runs of spaces, which is
correct for prose but destroys indentation in structured text (YAML, Python,
Markdown). Use clean(text, strip=False, collapse_spaces=False) or
profile="minimal" for those. See LIMITATIONS.md for this
and other deliberate design tradeoffs.
Quality & review
cleanmonkey is hardened with a competitive multi-model review methodology and a measurable release gate:
CONTRIBUTING.md— testing philosophy and the review-panel process.LIMITATIONS.md— intentional tradeoffs reviewers should not re-litigate.RELEASE_READINESS.md+release_readiness.json+scripts/readiness.py— the release rubric and convergence metric (python scripts/readiness.py).REVIEW_HISTORY.md— how the library was hardened.
License
MIT
Metadata
Release files for cleanmonkey 0.2.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 | |
|---|---|---|---|---|
| cleanmonkey-0.2.0.tar.gz | 33.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cleanmonkey-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.1 kB
Release files / cleanmonkey-0.2.0.tar.gz
| Download URL | cleanmonkey-0.2.0.tar.gz |
|---|---|
| Size | 33.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a0794bb74b7637d73b54d407f5c31a4cc210ee8299663a0d5c3d7b99710d8ab2
|
|
BLAKE2b-256 checksum How to use checksums |
f72f228c9cea05a45032aac6d71a89844d900b76af67ce3849ef4dab2906eca1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / cleanmonkey-0.2.0-py3-none-any.whl
| Download URL | cleanmonkey-0.2.0-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19f8d7b0c9a6f6dced558960f9db3aac7dc6dc66adfcf048a9a8df7a84739d46
|
|
BLAKE2b-256 checksum How to use checksums |
91a3fd2cfd5640d4b0ecff0aad63c5e3e01e2037a9d411ec2eb5fc4a2c04bbdb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for cleanmonkey 0.2.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
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.