wcwidth

Measures the displayed width of Unicode strings in a terminal, so CLI/TUI output lines up correctly across CJK, emoji, and combining characters.

Library
PyPI
v0.8.3
466stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
77/100Good
Development Activity84
Maintenance72
Community72
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
85/100Excellent
Architecture85
Code Quality88
Innovation78
Learning Curve90

wcwidth solves a problem every terminal application eventually hits: Python’s built-in string functions like str.ljust(), str.rjust(), str.center(), and textwrap.wrap() measure a string’s length by counting codepoints, not the number of terminal cells it actually occupies. Wide East Asian characters consume two cells, combining accents consume zero, and many emoji sequences (including ZWJ joins and variation selectors) don’t map cleanly to codepoint counts at all — so naive padding and wrapping produces visibly misaligned output.

The library is built in layers: low-level wcwidth() and wcswidth() functions precisely replicate the POSIX.1-2001/2008 wcwidth(3)/wcswidth(3) C library semantics for single characters and strings, returning -1 for control codes. On top of that sits width(), a higher-level function that also understands terminal escape sequences — SGR color codes, cursor movement, tabs, and OSC hyperlinks/text-sizing sequences — so it can measure real-world terminal output rather than just plain text. Drop-in replacements ljust(), rjust(), center(), wrap(), and clip() extend this correct-width logic to common string-formatting operations, including a “painter’s algorithm” for clipping text that contains cursor overtyping.

A distinguishing feature is per-terminal-emulator correction tables, generated by the companion ucs-detect project, which crowdsources measurement discrepancies across real terminal emulators (Windows Terminal, WezTerm, ghostty, contour, foot, and others) for Unicode grapheme clustering (DEC Private Mode 2027) support. wcstwidth() and width(term_program=...) apply these corrections automatically when a TERM_PROGRAM environment variable or terminal query response is available.

Because nearly every serious Python CLI and TUI project — REPLs, progress bars, table renderers, text editors — needs correct column math to avoid garbled output, wcwidth has become a near-universal transitive dependency in the Python terminal ecosystem.

What You Get

  • wcwidth() / wcswidth() - POSIX-compliant single-character and string width measurement, returning -1 for control codes
  • width() - higher-level measurement that understands ANSI/SGR color codes, cursor movement, tabs, and OSC hyperlink/text-sizing sequences
  • wcstwidth() and term_program corrections - per-terminal-emulator correction tables sourced from the ucs-detect crowdsourced compatibility project
  • ljust(), rjust(), center(), wrap(), clip() - drop-in, width-aware replacements for the standard string/textwrap justification and wrapping functions
  • iter_graphemes() / iter_graphemes_reverse() / grapheme_boundary_before() - Unicode Standard Annex #29 grapheme cluster iteration for cursor-safe navigation
  • iter_sequences() / strip_sequences() - splits or removes terminal escape sequences from text
  • Hyperlink / propagate_sgr - OSC 8 hyperlink parsing and SGR style propagation across wrapped/clipped line boundaries

Common Use Cases

  • Table and column renderers - CLI tools padding and aligning columns of mixed-width text (CJK, emoji, ASCII) so output lines up visually
  • REPLs and line editors - libraries needing accurate cursor column math to redraw prompts and handle backward/forward cursor movement over complex Unicode
  • Progress bars and TUI frameworks - measuring label and bar width in terminal cells rather than character counts to avoid layout drift
  • Text wrapping for terminal output - wrapping colored (SGR) or hyperlinked (OSC 8) text to a fixed column width without breaking escape sequences mid-line
  • Terminal emulator compatibility testing - the companion ucs-detect tooling uses wcwidth’s tables to benchmark real terminal emulator Unicode support

Under The Hood

Architecture wcwidth is organized as a thin, lazily-imported facade (wcwidth/__init__.py) over a set of focused modules: _wcwidth.py and _wcswidth.py implement the low-level POSIX-compatible single-character and string measurement; _width.py layers a higher-level width() on top that adds escape-sequence awareness via escape_sequences.py and control_codes.py; align.py, textwrap.py, and _clip.py build justification, wrapping, and column-based clipping (including a “painter’s algorithm” for cursor-overtyping sequences) on top of that. Static Unicode data (table_wide.py, table_zero.py, table_ambiguous.py, table_grapheme.py, table_vs15.py, table_vs16.py, table_mc.py) is generated from Unicode Character Database sources and consulted via bisearch.py’s binary search rather than embedded in logic, keeping measurement code separate from measurement data. Per-terminal-emulator corrections live in table_overrides.py and table_term_programs.py, resolved through _constants.py. A __lazy_modules__ mechanism defers importing most submodules until first use, keeping baseline import cost low for a library that is a near-universal transitive dependency.

Tech Stack Pure Python with zero runtime dependencies, targeting Python 3.8 through the 3.15 pre-release, built and packaged with hatchling. The project maintains a fairly large surface of static generated data tables sourced from the Unicode Character Database and from its own companion ucs-detect project (crowdsourced real-terminal-emulator compatibility results), which feed the correction logic in table_overrides.py. No web framework, database, or external service integration — this is a self-contained string-measurement library.

Code Quality The tests/ directory contains an extensive pytest suite (test_core, test_ambiguous, test_clip, test_clip_cjk_emoji, test_clip_overtyping, test_emojis, test_grapheme, test_hyperlink, test_justify, test_sgr_state, test_term_overrides, test_text_sizing, test_textwrap, test_ucslevel, test_width, test_benchmarks) covering edge cases like empty strings, control codes, and terminal-specific overrides. CI (.github/workflows/ci.yml) runs the suite across Python 3.8-3.13 on Ubuntu, Windows, and macOS, plus a separate lint matrix (flake8, isort, pydocstyle, pylint, docformatter, codespell) and a dedicated mypy type-checking job — py.typed is shipped, marking the package as fully typed. A CodeQL workflow and a codspeed benchmark workflow are also configured, indicating active attention to both security and performance regressions.

API Design The public API is intentionally narrow and documented via __all__ in __init__.py — a handful of top-level functions (wcwidth, wcswidth, width, ljust/rjust/center, wrap, clip) cover the vast majority of use cases with sensible defaults (control_codes='parse', ambiguous_width=1), while power users can opt into stricter or faster modes (control_codes='strict'/'ignore') and terminal-specific correction (term_program=) without changing call sites. The library goes out of its way to preserve backward compatibility with pre-0.7.0 import paths (from wcwidth.wcwidth import ...) via an explicit shim, and every public function has runnable doctest-style examples in the README, lowering the barrier to correct usage.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search