Pre-commit hook to detect and fix non-ASCII Unicode characters
Project description
check-unicode
A pre-commit hook to detect and fix non-ASCII Unicode characters in text files.
Catches smart quotes, em dashes, fancy spaces, dangerous invisible characters (Trojan Source bidi attacks, zero-width chars), and other copy-paste artifacts.
Installation
As a pre-commit hook
repos:
- repo: https://github.com/mit-d/check-unicode
rev: v0.6.0
hooks:
- id: check-unicode
# or for auto-fix:
- id: fix-unicode
Standalone
pip install check-unicode
check-unicode path/to/file.txt
Usage
check-unicode [OPTIONS] [FILES...]
| Flag | Description | Default |
|---|---|---|
--fix |
Replace known offenders with ASCII, exit 1 if changed | off |
--allow-range RANGE |
Allow a Unicode range (e.g. U+00A0-U+00FF). Repeat. |
none |
--allow-codepoint CP |
Allow codepoints (e.g. U+00B0). Repeat/comma-sep. |
none |
--allow-category CAT |
Allow Unicode category (e.g. Sc). Repeatable. |
none |
--allow-printable |
Allow all printable chars (only flag invisibles) | off |
--allow-script SCRIPT |
Allow Unicode script (e.g. Latin). Repeatable. |
none |
--check-confusables |
Detect mixed-script homoglyph/confusable characters | off |
--severity LEVEL |
error (exit 1) or warning (print, exit 0) |
error |
--no-color |
Disable ANSI color | auto-detect |
--config FILE |
Path to TOML config | auto-discover |
-q / --quiet |
Summary only | off |
-V / --version |
Print version |
What it catches
- Smart quotes:
\u201c\u201d\u2018\u2019and variants - Dashes: em dash
\u2014, en dash\u2013, minus sign\u2212 - Fancy spaces: non-breaking space, em space, thin space, etc.
- Ellipsis:
\u2026 - Dangerous invisible characters (always flagged):
- Bidi control (Trojan Source CVE-2021-42574): U+202A-202E, U+2066-2069
- Zero-width: U+200B-200F, U+FEFF (mid-file), U+2060-2064, U+180E
- Replacement character: U+FFFD
- Confusable homoglyphs (with
--check-confusables):- Mixed-script identifiers where minority-script chars look like Latin
- Cyrillic/Greek/Armenian letters that visually resemble Latin letters
- e.g. Cyrillic
a(U+0430) mixed with Latinccess_level
Auto-fix
--fix replaces known offenders with ASCII equivalents:
| Unicode | Replacement |
|---|---|
| Smart quotes | ' or " |
| En/em dashes | -- |
| Minus sign | - |
| Fancy spaces | |
| Ellipsis | ... |
Dangerous invisible characters are never auto-fixed -- they require manual review.
Configuration
Create .check-unicode.toml or add to pyproject.toml:
[tool.check-unicode]
allow-codepoints = ["U+00B0", "U+2192"]
allow-ranges = ["U+00A0-U+00FF"]
allow-categories = ["Sc"]
allow-printable = true
allow-scripts = ["Latin", "Cyrillic"]
check-confusables = true
severity = "error"
Per-file overrides
Use [[tool.check-unicode.overrides]] to apply different settings per file
pattern. Each entry requires a files key with a list of glob patterns and can
set any of the options above.
Allow-lists (allow-codepoints, allow-ranges, allow-categories,
allow-printable, allow-scripts) are additive -- they merge with the
global config. severity and check-confusables replace the global value
for matching files.
# Relax rules for documentation
[[tool.check-unicode.overrides]]
files = ["docs/*.md", "*.rst"]
allow-printable = true
severity = "warning"
# Allow non-Latin scripts in i18n files
[[tool.check-unicode.overrides]]
files = ["i18n/**"]
allow-scripts = ["Cyrillic", "Han"]
check-confusables = false
Output
path/to/file.txt:42:17: U+201C LEFT DOUBLE QUOTATION MARK [Ps]
He said \u201chello\u201d to the crowd
^
Found 5 non-ASCII characters in 2 files (3 fixable, 1 dangerous)
Development
uv venv && uv sync --group dev
.venv/bin/pytest -v --cov
.venv/bin/ruff check src/ tests/
uvx ty check src/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file check_unicode-0.6.0.tar.gz.
File metadata
- Download URL: check_unicode-0.6.0.tar.gz
- Upload date:
- Size: 26.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33ec600d93760309d1d15259e090346b4e5f3e2c8d4b3f63f7f94d65ec8530f3
|
|
| MD5 |
fc5a158d126a9b9e8cc6aa56a5f08d60
|
|
| BLAKE2b-256 |
2bde6a40967aa126f790785cce1f8a1435b384b701a2d85649933fed5f85d266
|
Provenance
The following attestation bundles were made for check_unicode-0.6.0.tar.gz:
Publisher:
release.yml on mit-d/check-unicode
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
check_unicode-0.6.0.tar.gz -
Subject digest:
33ec600d93760309d1d15259e090346b4e5f3e2c8d4b3f63f7f94d65ec8530f3 - Sigstore transparency entry: 1194660606
- Sigstore integration time:
-
Permalink:
mit-d/check-unicode@9d217f6f91d9e2de6bc31b54575ff35e595173f5 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/mit-d
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9d217f6f91d9e2de6bc31b54575ff35e595173f5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file check_unicode-0.6.0-py3-none-any.whl.
File metadata
- Download URL: check_unicode-0.6.0-py3-none-any.whl
- Upload date:
- Size: 25.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b81e7bd6d8b8716ebe9a23707c82ce7e95ee1411f4c35a5af921aa192d6d6b19
|
|
| MD5 |
875118e1acf1bfe8b6b0d4115bdd9fe4
|
|
| BLAKE2b-256 |
1b32d7c9e3a03f74f14e35e5cd3bb40c083b9a937b2f31182065cc32cc5e14b6
|
Provenance
The following attestation bundles were made for check_unicode-0.6.0-py3-none-any.whl:
Publisher:
release.yml on mit-d/check-unicode
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
check_unicode-0.6.0-py3-none-any.whl -
Subject digest:
b81e7bd6d8b8716ebe9a23707c82ce7e95ee1411f4c35a5af921aa192d6d6b19 - Sigstore transparency entry: 1194660607
- Sigstore integration time:
-
Permalink:
mit-d/check-unicode@9d217f6f91d9e2de6bc31b54575ff35e595173f5 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/mit-d
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9d217f6f91d9e2de6bc31b54575ff35e595173f5 -
Trigger Event:
push
-
Statement type: