Skip to main content

Pre-commit hook to detect and fix non-ASCII Unicode characters

Project description

check-unicode

CI License: MIT Python 3.11+

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.5.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 \u2019 and 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 Latin ccess_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 pip install -e ".[dev]"
.venv/bin/pytest -v --cov
.venv/bin/ruff check src/ tests/
.venv/bin/mypy src/

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

check_unicode-0.5.0.tar.gz (21.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

check_unicode-0.5.0-py3-none-any.whl (21.4 kB view details)

Uploaded Python 3

File details

Details for the file check_unicode-0.5.0.tar.gz.

File metadata

  • Download URL: check_unicode-0.5.0.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for check_unicode-0.5.0.tar.gz
Algorithm Hash digest
SHA256 67f0e22355fb2079e914f34b97747578688220db06697270983557e1c6312c7d
MD5 0420ee5b0c5db7841174bd78644d2354
BLAKE2b-256 7231ff942eb0e817130d029706500ebceb4c891d112589d951fefa7dd96aeeea

See more details on using hashes here.

Provenance

The following attestation bundles were made for check_unicode-0.5.0.tar.gz:

Publisher: release.yml on mit-d/check-unicode

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file check_unicode-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: check_unicode-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 21.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for check_unicode-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3614e0fd91398c34d15ea2bc4a68de440bba5f66e4e8c215c8021567aef3fe89
MD5 5e95cc7ac5b9e143dcacaf64f6c85cca
BLAKE2b-256 0fd8551406935e299f9056346843cc3c7575d2cc5bf60d3a4ec8bf142a1c01cc

See more details on using hashes here.

Provenance

The following attestation bundles were made for check_unicode-0.5.0-py3-none-any.whl:

Publisher: release.yml on mit-d/check-unicode

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page