590 lines
23 KiB
Plaintext
590 lines
23 KiB
Plaintext
Metadata-Version: 2.4
|
|
Name: wcwidth
|
|
Version: 0.9.2
|
|
Summary: Measures the displayed width of unicode strings in a terminal
|
|
Author-email: Jeff Quast <contact@jeffquast.com>
|
|
Project-URL: Homepage, https://github.com/jquast/wcwidth
|
|
Keywords: cjk,combining,console,eastasian,emoji,emulator,terminal,unicode,wcswidth,wcwidth,xterm
|
|
Classifier: Development Status :: 5 - Production/Stable
|
|
Classifier: Environment :: Console
|
|
Classifier: Intended Audience :: Developers
|
|
Classifier: License :: OSI Approved :: MIT License
|
|
Classifier: Natural Language :: English
|
|
Classifier: Operating System :: POSIX
|
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
Classifier: Programming Language :: Python :: 3.9
|
|
Classifier: Programming Language :: Python :: 3.10
|
|
Classifier: Programming Language :: Python :: 3.11
|
|
Classifier: Programming Language :: Python :: 3.12
|
|
Classifier: Programming Language :: Python :: 3.13
|
|
Classifier: Programming Language :: Python :: 3.14
|
|
Classifier: Programming Language :: Python :: 3.15
|
|
Classifier: Topic :: Software Development :: Internationalization
|
|
Classifier: Topic :: Software Development :: Libraries
|
|
Classifier: Topic :: Software Development :: Localization
|
|
Classifier: Topic :: Terminals
|
|
Classifier: Typing :: Typed
|
|
Requires-Python: >=3.9
|
|
Description-Content-Type: text/x-rst
|
|
License-File: LICENSE
|
|
Dynamic: license-file
|
|
|
|
|pypi_downloads| |codecov| |license|
|
|
|
|
============
|
|
Introduction
|
|
============
|
|
|
|
This Python and C11 library is mainly for CLI/TUI programs that carefully produce output for
|
|
Terminals. This page is about the Python library, see libwcwidth_ for the portable C11 library.
|
|
|
|
Installation
|
|
------------
|
|
|
|
The stable version of this package is maintained on pypi, install or upgrade, using pip::
|
|
|
|
pip install --upgrade wcwidth
|
|
|
|
Problem
|
|
-------
|
|
|
|
All Python string-formatting functions, `textwrap.wrap()`_, `str.ljust()`_, `str.rjust()`_, and
|
|
`str.center()`_ **incorrectly** measure the displayed width of a string as equal to the number of
|
|
their codepoints.
|
|
|
|
Some examples of **incorrect results**:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # result consumes 16 total cells, 11 expected,
|
|
>>> 'コンニチハ'.rjust(11, 'X')
|
|
'XXXXXXコンニチハ'
|
|
|
|
>>> # combining acute accent: result consumes 5 total cells, 6 expected,
|
|
>>> 'cafe\u0301'.center(6, 'X')
|
|
'caféX'
|
|
|
|
Solution
|
|
--------
|
|
|
|
The lowest-level functions in this library are derived from POSIX.1-2001 and POSIX.1-2008
|
|
`wcwidth(3)`_ and `wcswidth(3)`_, which this library precisely copies by interface as `wcwidth()`_
|
|
and `wcswidth()`_. These functions return -1 when C0 and C1 control codes are present.
|
|
|
|
An easy-to-use `width()`_ function is provided as a wrapper of `wcswidth()`_ that is also capable of
|
|
measuring most terminal control codes and sequences, like colors, bold, tabstops, and horizontal
|
|
cursor movement. `width()`_ argument ``term_program`` may provide more accurate terminal measurement
|
|
Corrections_ as a wrapper of `wcstwidth()`_.
|
|
|
|
Text-justification is solved by the sequence-aware functions `ljust()`_, `rjust()`_, `center()`_,
|
|
and the grapheme-aware function `wrap()`_, serving as drop-in replacements to python standard
|
|
functions.
|
|
|
|
The `clip()`_ function extracts substrings by their displayed column positions, and
|
|
`strip_sequences()`_ removes terminal escape sequences from text altogether.
|
|
|
|
The iterator functions `iter_graphemes()`_ and `iter_sequences()`_ allow for careful navigation of
|
|
grapheme and terminal control sequence boundaries as required by editors or REPLs with cursor
|
|
control. `iter_graphemes_reverse()`_ and `grapheme_boundary_before()`_ are necessary for backward
|
|
cursor control over complex unicode.
|
|
|
|
Discrepancies
|
|
-------------
|
|
|
|
You may find that support *varies* for complex unicode sequences or codepoints.
|
|
|
|
This library may be considered to presume the terminal is enabled for DEC Private Mode 2027
|
|
("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
|
|
is often the default mode for those terminals that support it.
|
|
|
|
This library does not support any specific "legacy width" measurement by API, but it does provide
|
|
Corrections_ for terminals using "legacy width" measurements or are without basic emoji or grapheme
|
|
support.
|
|
|
|
See also:
|
|
|
|
- `terminal-unicode-core.tex`_ (2021)
|
|
- `Grapheme Clusters and Terminal Emulators`_ (2023)
|
|
- `State of Terminal Emulators in 2025`_
|
|
- `Perfecting Terminal Character Width Using Correction Tables`_ (2026)
|
|
|
|
The `jquast/ucs-detect`_ project publishes automatic results of compliance to our standard for Wide
|
|
character, Languages, grapheme clustering, complex or combining scripts, emojis, zero-width joiner,
|
|
variations, and regional indicator (flags) as a `General Tabulated Summary`_ by terminal emulator
|
|
software and version. The results of the ucs-detect project create our correction tables.
|
|
|
|
========
|
|
Overview
|
|
========
|
|
|
|
A brief overview, through examples, for all of the public API functions.
|
|
|
|
Full API Documentation at https://wcwidth.readthedocs.io/en/latest/api.html
|
|
|
|
wcwidth()
|
|
---------
|
|
|
|
Measures width of a single codepoint,
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # '♀' narrow emoji
|
|
>>> wcwidth.wcwidth('\u2640')
|
|
1
|
|
|
|
Use function `wcwidth()`_ to determine the length of a *single unicode character*.
|
|
|
|
See specification_ of character measurements. Note that ``-1`` is returned for control codes.
|
|
|
|
wcswidth()
|
|
----------
|
|
|
|
Measures width of a string, returns -1 for control codes.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # '♀️' emoji w/vs-16
|
|
>>> wcwidth.wcswidth('\u2640\ufe0f')
|
|
2
|
|
|
|
Use function `wcswidth()`_ to determine the length of many, a *string of unicode characters*.
|
|
|
|
See specification_ of character measurements. Note that ``-1`` is returned if control codes occurs
|
|
anywhere in the string.
|
|
|
|
wcstwidth()
|
|
-----------
|
|
|
|
`wcstwidth()`_ is a variant of `wcswidth()`_ that provides terminal-specific Corrections_, reading
|
|
``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
|
|
XTVERSION_ or ENQ_ response:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # '♀️' emoji w/vs-16, uncorrected:
|
|
>>> wcwidth.wcswidth('\u2640\ufe0f')
|
|
2
|
|
>>> # corrected,
|
|
>>> wcwidth.wcstwidth('\u2640\ufe0f', term_program='vte')
|
|
1
|
|
|
|
width()
|
|
-------
|
|
|
|
Use function `width()`_ to measure a string with improved handling of ``control_codes`` and
|
|
measurement Corrections_ through ``term_program``:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # same support as wcswidth(), eg. regional indicator flag:
|
|
>>> wcwidth.width('\U0001F1FF\U0001F1FC')
|
|
2
|
|
>>> # set term_program=True to use wcstwidth()
|
|
>>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program=True)
|
|
1
|
|
>>> # or set term_program for measurement of a specific terminal
|
|
>>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program='contour')
|
|
2
|
|
>>> # but also supports sequences, like SGR colored text, "WARN", followed by reset
|
|
>>> wcwidth.width('\x1b[38;2;255;150;100mWARN\x1b[0m')
|
|
4
|
|
>>> # tabs are measured as though the string begins at a tabstop,
|
|
>>> wcwidth.width('\t', tabsize=4)
|
|
4
|
|
>>> # or, all control characters can be ignored (including tab)
|
|
>>> wcwidth.width('\t\n\a\r', control_codes='ignore')
|
|
0
|
|
>>> # sequences with "indeterminate" effects like Home + Clear are zero-width
|
|
>>> wcwidth.width('\x1b[H\x1b[2J')
|
|
0
|
|
>>> # horizontal cursor movements are parsed,
|
|
>>> wcwidth.width('hello\b\b\b\b\bworld')
|
|
5
|
|
>>> wcwidth.width('hello\x1b[5Dworld')
|
|
5
|
|
>>> # or ignored,
|
|
>>> wcwidth.width('hello\x1b[5Dworld', control_codes='ignore')
|
|
10
|
|
>>> # Measure width of text using kitty text sizing protocol (OSC 66),
|
|
>>> width('\x1b]66;w=2;XY\x07')
|
|
2
|
|
>>> # Scaled text sizing: each grapheme occupies 'scale' cells
|
|
>>> width('\x1b]66;s=2;ABC\x07')
|
|
6
|
|
|
|
Use ``control_codes='ignore'`` when the input is known not to contain any control characters or
|
|
terminal sequences for slightly improved performance. Note that TAB (``'\t'``) is a control
|
|
character and is also ignored, you may want to use `str.expandtabs()`_, first.
|
|
|
|
Use ``control_codes='strict'`` when input is known to contain some control sequences, such as
|
|
SGR color, bold, hyperlinks and cursor movement. Any sequence that cannot be accurately parsed
|
|
for horizontal measurement, such as clearing the screen, vertical, or absolute cursor movement will
|
|
raise ``ValueError``:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # or, raise ValueError for "indeterminate" effects using control_codes='strict'
|
|
>>> wcwidth.width('\n', control_codes='strict')
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Vertical movement character 0xa at position 0
|
|
|
|
|
|
>>> wcwidth.width('\x1b[H\x1b[2J', control_codes='strict')
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Indeterminate cursor sequence at position 0, '\x1b[H'
|
|
|
|
|
|
>>> # cursor left movement beyond string start raises in strict mode,
|
|
>>> wcwidth.width('a\x1b[5Da', control_codes='strict')
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Cursor left movement at position 1 would move 5 cells left from column 1, exceeding string start
|
|
|
|
iter_sequences()
|
|
----------------
|
|
|
|
Iterates through text, segmented by terminal sequence,
|
|
|
|
.. code-block:: python
|
|
|
|
>>> list(wcwidth.iter_sequences('hello'))
|
|
[('hello', False)]
|
|
>>> list(wcwidth.iter_sequences('\x1b[31mred\x1b[0m'))
|
|
[('\x1b[31m', True), ('red', False), ('\x1b[0m', True)]
|
|
|
|
Use `iter_sequences()`_ to split text into segments of plain text and escape sequences. Each tuple
|
|
contains the segment string and a boolean indicating whether it is an escape sequence (``True``) or
|
|
text (``False``).
|
|
|
|
iter_graphemes()
|
|
----------------
|
|
|
|
Use `iter_graphemes()`_ to iterate over *grapheme clusters* of a string.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> from wcwidth import iter_graphemes
|
|
>>> # ok + Regional Indicator 'Z', 'W' (Zimbabwe)
|
|
>>> list(wcwidth.iter_graphemes('ok\U0001F1FF\U0001F1FC'))
|
|
['o', 'k', '🇿🇼']
|
|
|
|
>>> # cafe + combining acute accent
|
|
>>> list(wcwidth.iter_graphemes('cafe\u0301'))
|
|
['c', 'a', 'f', 'é']
|
|
|
|
>>> # ok + Emoji Man + ZWJ + Woman + ZWJ + Girl
|
|
>>> list(wcwidth.iter_graphemes('ok\U0001F468\u200D\U0001F469\u200D\U0001F467'))
|
|
['o', 'k', '👨\u200d👩\u200d👧']
|
|
|
|
A grapheme cluster is what a user perceives as a single character, even if it is composed of
|
|
multiple Unicode codepoints. This function implements `Unicode Standard Annex #29`_ grapheme cluster
|
|
boundary rules.
|
|
|
|
ljust()
|
|
-------
|
|
|
|
Use `ljust()`_ as replacement of `str.ljust()`_:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> 'コンニチハ'.ljust(11, '*') # don't do this
|
|
'コンニチハ******'
|
|
>>> wcwidth.ljust('コンニチハ', 11, '*') # do this!
|
|
'コンニチハ*'
|
|
|
|
rjust()
|
|
-------
|
|
|
|
Use `rjust()`_ as replacement of `str.rjust()`_:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> 'コンニチハ'.rjust(11, '*') # don't do this
|
|
'******コンニチハ'
|
|
>>> wcwidth.rjust('コンニチハ', 11, '*') # do this!
|
|
'*コンニチハ'
|
|
|
|
center()
|
|
--------
|
|
|
|
Use `center()`_ as replacement of `str.center()`_:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> 'cafe\u0301'.center(6, '*') # don't do this
|
|
'café*'
|
|
>>> wcwidth.center('cafe\u0301', 6, '*')
|
|
'*café*' # do this!
|
|
|
|
wrap()
|
|
------
|
|
|
|
Use function `wrap()`_ to wrap text containing terminal sequences, Unicode grapheme
|
|
clusters, and wide characters to a given display width.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> from wcwidth import wrap
|
|
>>> # Basic wrapping
|
|
>>> wrap('hello world', 5)
|
|
['hello', 'world']
|
|
|
|
>>> # Wrapping CJK text (each character is 2 cells wide)
|
|
>>> wrap('コンニチハ', 4)
|
|
['コン', 'ニチ', 'ハ']
|
|
|
|
>>> # Text with ANSI color sequences - SGR codes are propagated by default
|
|
>>> # Each line ends with reset, next line starts with restored style
|
|
>>> wrap('\x1b[1;31mhello world\x1b[0m', 5)
|
|
['\x1b[1;31mhello\x1b[0m', '\x1b[1;31mworld\x1b[0m']
|
|
|
|
clip()
|
|
------
|
|
|
|
Use `clip()`_ to extract a substring by column positions, preserving terminal sequences.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> from wcwidth import clip
|
|
>>> # Wide characters split to Narrow boundaries using fillchar=' '
|
|
>>> clip('中文字', 0, 3)
|
|
'中 '
|
|
>>> clip('中文字', 1, 5, fillchar='.')
|
|
'.文.'
|
|
|
|
>>> # 'end' defaults to -1, meaning "to the end of the line"
|
|
>>> clip('中文字', 1)
|
|
' 文字'
|
|
>>> clip('\x1b[1;31mHello world\x1b[0m', 6)
|
|
'\x1b[1;31mworld\x1b[0m'
|
|
|
|
>>> # SGR codes are propagated by default - result begins with active style
|
|
>>> # and ends with reset if styles are active
|
|
>>> clip('\x1b[1;31mHello world\x1b[0m', 6, 11)
|
|
'\x1b[1;31mworld\x1b[0m'
|
|
|
|
>>> # Disable SGR propagation to preserve sequence order outside of clip boundary
|
|
>>> clip('\x1b[31m中文\x1b[32m', 0, 3, propagate_sgr=False)
|
|
'\x1b[31m中 \x1b[32m'
|
|
|
|
>>> # Cursor-left overwrites previous text (painter's algorithm)
|
|
>>> clip('hello\x1b[2DXY', 0, 5)
|
|
'helXY'
|
|
>>> # Carriage return resets to column 0, overwriting earlier cells
|
|
>>> clip('abc\rXY', 0, 5)
|
|
'XYc'
|
|
|
|
>>> # even OSC 8 hyperlink text may be clipped, 'Click This link' -> 'is link' !
|
|
>>> clip('\x1b]8;;http://example.com\x07Click This link\x1b]8;;\x07', 8, 15)
|
|
'\x1b]8;;http://example.com\x07is link\x1b]8;;\x07'
|
|
|
|
>>> # and OSC 66 kitty text sizing, supporting width and scale, 'Look' -> '...ook'
|
|
>>> clip('\x1b]66;w=4:s=4;Look\x07', 1, 16, fillchar='.')
|
|
'...\x1b]66;s=4:w=3;ook\x07'
|
|
|
|
Use ``overtyping=False`` when the input is known not to contain any cursor movement characters
|
|
(``\b``, ``\r``, ``CSI C``, ``CSI D``, ``CSI G``) for improved performance. When
|
|
``overtyping=None`` (default), a slower "Painter's algorithm" may be used after testing for the
|
|
presence of these characters. ``overtyping`` has no effect when ``control_codes='ignore'``.
|
|
|
|
strip_sequences()
|
|
-----------------
|
|
|
|
Use `strip_sequences()`_ to remove all terminal escape sequences from text.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> from wcwidth import strip_sequences
|
|
>>> strip_sequences('\x1b[31mred\x1b[0m')
|
|
'red'
|
|
|
|
.. _ambiguous_width:
|
|
|
|
Ambiguous Width
|
|
---------------
|
|
|
|
Some Unicode characters have "East Asian Ambiguous" (A) width. These characters display as 1 cell by
|
|
default, matching Western terminal contexts, but many CJK (Chinese, Japanese, Korean) environments
|
|
may have a preference for 2 cells. This is often found as boolean option, "Ambiguous width as wide"
|
|
in Terminal Emulator software preferences.
|
|
|
|
The ``ambiguous_width`` parameter is available on all width-measuring functions: `wcwidth()`_,
|
|
`wcswidth()`_, `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
|
|
|
|
By default, wcwidth treats ambiguous characters as narrow (width 1). For CJK environments where your
|
|
terminal is configured to display ambiguous characters as double-width, pass ``ambiguous_width=2``:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> # CIRCLED DIGIT ONE - ambiguous width
|
|
>>> wcwidth.width('\u2460')
|
|
1
|
|
>>> wcwidth.width('\u2460', ambiguous_width=2)
|
|
2
|
|
|
|
**Terminal Detection**
|
|
|
|
The most reliable method to detect whether a terminal profile is set for "Ambiguous width as wide"
|
|
mode is to display an ambiguous character surrounded by a pair of Cursor Position Report (CPR)
|
|
queries with a terminal in cooked or raw mode, and to parse the responses for their ``(y, x)``
|
|
locations and measure the difference ``x``.
|
|
|
|
This code should also be careful to check whether it is attached to a terminal and be careful of
|
|
possible timeout, slow network, or non-response when working with "dumb terminals" like a CI build.
|
|
|
|
`jquast/blessed`_ library provides such a helping `Terminal.detect_ambiguous_width()`_ method:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> import blessed, functools
|
|
>>> # Detect terminal ambiguous width as wide (2) or narrow (1)
|
|
>>> ambiguous_width = blessed.Terminal().detect_ambiguous_width()
|
|
>>> # Define a new 'width' function with this argument
|
|
>>> awidth = functools.partial(wcwidth.width, ambiguous_width=ambiguous_width)
|
|
>>> # result depends on attached terminal mode
|
|
>>> awidth('\u2460')
|
|
1
|
|
|
|
Corrections
|
|
-----------
|
|
|
|
Corrections may be automatically applied depending on the detected or given terminal software name
|
|
beginning with wcwidth release 0.8.0. This allows to correct widths for terminal software that
|
|
differs from the python wcwidth specification_. These corrections are sourced from the
|
|
`jquast/ucs-detect`_ project.
|
|
|
|
The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
|
|
`width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
|
|
|
|
`wcstwidth()`_ defaults to ``term_program=True``, auto-detecting the terminal from the
|
|
``TERM_PROGRAM`` or ``TERM`` environment variable. All other functions default to
|
|
``term_program=False``, disabling corrections. Use ``term_program=True`` for automatic
|
|
detection by environment values of ``TERM`` and ``TERM_PROGRAM``.
|
|
|
|
.. code-block:: python
|
|
|
|
# VTE terminals (Gnome Terminal Et al.) still render trigrams as narrow (1 cell), but their
|
|
# definition was changed to wide in Unicode 16 (September 2024).
|
|
>>> wcwidth.wcswidth('\u2630')
|
|
2
|
|
>>> wcwidth.wcstwidth('\u2630', term_program='vte')
|
|
1
|
|
|
|
# account for Alacritty non-support of emoji ZWJ:
|
|
# man + ZWJ + woman + ZWJ + girl + ZWJ + boy
|
|
>>> family = '\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466'
|
|
>>> wcwidth.wcswidth(family)
|
|
2
|
|
>>> wcwidth.wcstwidth(family, term_program='alacritty')
|
|
8
|
|
|
|
Only detectable_ terminals are included: those that identify themselves by XTVERSION_, ENQ_, any
|
|
``TERM_PROGRAM`` or a unique ``TERM`` environment value. For the most accurate correction tables,
|
|
query the terminal's software version via XTVERSION_ (``CSI > q``) using a higher-level interactive
|
|
terminal library like `jquast/blessed`_:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> import blessed, wcwidth
|
|
>>> term = blessed.Terminal()
|
|
>>> sw_ver = term.get_software_version()
|
|
>>> print(sw_ver)
|
|
SoftwareVersion(name='VTE', version='7600')
|
|
>>> wcwidth.width('\u2630', term_program=sw_ver.name)
|
|
1
|
|
|
|
This is important because ``TERM_PROGRAM`` is not forwarded for remote hosts, like SSH, and many
|
|
terminals may only be identified using XTVERSION_ or ENQ_. Use `list_term_programs()`_ to see all
|
|
recognized names:
|
|
|
|
.. BEGIN_LIST_TERM_PROGRAMS
|
|
.. code-block:: python
|
|
|
|
>>> wcwidth.list_term_programs()
|
|
('absolutetelnet/ssh', 'alacritty', 'apple_terminal', 'bobcat', 'contour',
|
|
'extraterm', 'foot', 'ghostty', 'hyper', 'iterm.app', 'iterm2', 'kitty',
|
|
'konsole', 'mintty', 'mlterm', 'pterm', 'putty', 'rio', 'rxvt',
|
|
'rxvt-unicode-256color', 'st', 'st-256color', 'tabby', 'terminology',
|
|
'urxvt', 'vscode', 'vte', 'warp', 'warpterminal', 'wezterm', 'xterm',
|
|
'xterm-ghostty', 'xterm-kitty', 'xterm.js')
|
|
|
|
.. END_LIST_TERM_PROGRAMS
|
|
|
|
``term_program=False`` (the default for `width()`_, `ljust()`_, `rjust()`_, `center()`_,
|
|
`wrap()`_, and `clip()`_) disables terminal corrections.
|
|
|
|
For automatic tests and other purposes that require cross-environment consistency, set static values
|
|
or unset ``TERM`` and ``TERM_PROGRAM`` environment values, such as in ``conftest.py`` with pytest:
|
|
|
|
.. code-block:: python
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_term_program():
|
|
"""unset TERM/TERM_PROGRAM before each test."""
|
|
saved_term = os.environ.pop('TERM', None)
|
|
saved_tprog = os.environ.pop('TERM_PROGRAM', None)
|
|
yield
|
|
if saved_term is not None:
|
|
os.environ['TERM'] = saved_term
|
|
if saved_tprog is not None:
|
|
os.environ['TERM_PROGRAM'] = saved_tprog
|
|
|
|
==================
|
|
More documentation
|
|
==================
|
|
|
|
Developer documentation, for building and contributing to this project, at
|
|
https://wcwidth.readthedocs.io/en/latest/developing.html
|
|
|
|
Projects using wcwidth, and implementations in other languages, at
|
|
https://wcwidth.readthedocs.io/en/latest/related.html
|
|
|
|
.. _`specification`: https://wcwidth.readthedocs.io/en/latest/specs.html
|
|
.. _`jquast/blessed`: https://github.com/jquast/blessed
|
|
.. _`wcwidth(3)`: https://man7.org/linux/man-pages/man3/wcwidth.3.html
|
|
.. _`wcswidth(3)`: https://man7.org/linux/man-pages/man3/wcswidth.3.html
|
|
.. _`jquast/ucs-detect`: https://github.com/jquast/ucs-detect
|
|
.. _`textwrap.wrap()`: https://docs.python.org/3/library/textwrap.html#textwrap.wrap
|
|
.. _`str.ljust()`: https://docs.python.org/3/library/stdtypes.html#str.ljust
|
|
.. _`str.rjust()`: https://docs.python.org/3/library/stdtypes.html#str.rjust
|
|
.. _`str.center()`: https://docs.python.org/3/library/stdtypes.html#str.center
|
|
.. _`str.expandtabs()`: https://docs.python.org/3/library/stdtypes.html#str.expandtabs
|
|
.. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#general-tabulated-summary
|
|
.. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
|
|
.. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
|
|
.. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
|
|
.. _`width()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.width
|
|
.. _`iter_graphemes()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes
|
|
.. _`iter_graphemes_reverse()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes_reverse
|
|
.. _`grapheme_boundary_before()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.grapheme_boundary_before
|
|
.. _`ljust()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.ljust
|
|
.. _`rjust()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.rjust
|
|
.. _`center()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.center
|
|
.. _`wrap()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wrap
|
|
.. _`clip()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.clip
|
|
.. _`strip_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.strip_sequences
|
|
.. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
|
|
.. _`list_term_programs()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_term_programs
|
|
.. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
|
|
.. _`Terminal.detect_ambiguous_width()`: https://blessed.readthedocs.io/en/latest/api/terminal.html#blessed.terminal.Terminal.detect_ambiguous_width
|
|
.. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
|
|
.. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
|
|
.. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
|
|
.. _`Perfecting Terminal Character Width Using Correction Tables`: https://www.jeffquast.com/post/perfecting-terminal-character-width-using-correction-tables/
|
|
.. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
|
|
.. _ENQ: https://documentation.help/PuTTY/config-answerback.html
|
|
.. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
|
|
.. _libwcwidth: https://wcwidth.readthedocs.io/en/latest/libwcwidth.html
|
|
.. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi
|
|
:alt: Downloads
|
|
:target: https://pypi.org/project/wcwidth/
|
|
.. |codecov| image:: https://codecov.io/gh/jquast/wcwidth/branch/master/graph/badge.svg
|
|
:alt: codecov.io Code Coverage
|
|
:target: https://app.codecov.io/gh/jquast/wcwidth/
|
|
.. |license| image:: https://img.shields.io/pypi/l/wcwidth.svg
|
|
:target: https://pypi.org/project/wcwidth/
|
|
:alt: MIT License
|
|
|