wcwidth
Measures the displayed width of Unicode strings in a terminal, so CLI/TUI output lines up correctly across CJK, emoji, and combining characters.
Repository Health
Technical Analysis
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.
Used by 5 apps in this directory
argilla
AI Development · Data Engineering
Collaborate on high-quality AI training data with a self-hosted annotation platform built for LLMs, NLP, and multimodal models.
knowhere
AI Development · Developer Tools
Transform messy, unstructured documents into persistent, navigable memory that AI agents can actually use.
SWIRL
Search · Databases · Data Engineering
Federated AI search and RAG across 100+ enterprise sources—no data extraction, no vector database required.
Taiga Back
Project Management · Developer Tools
Self-hosted agile project management backend with Scrum, Kanban, issue tracking, and a full REST API — built on Django and PostgreSQL.
TiDB
Databases · AI Development
AI-Native Distributed SQL Database for Agentic Workloads