wcwidth 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wcwidth-0.6.0 → wcwidth-0.7.0}/PKG-INFO +136 -33
- wcwidth-0.7.0/bin/show-sequences +23 -0
- wcwidth-0.7.0/bin/strip-sequences +18 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/bin/update-tables.py +11 -6
- {wcwidth-0.6.0 → wcwidth-0.7.0}/bin/verify-table-integrity.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/bin/wcwidth-browser.py +9 -9
- {wcwidth-0.6.0 → wcwidth-0.7.0}/bin/wcwidth-libc-comparator.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/code_templates/grapheme_table.py.j2 +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/code_templates/python_table.py.j2 +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/code_templates/unicode_versions.py.j2 +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/api.rst +8 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/intro.rst +135 -32
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/requirements.txt +9 -10
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/specs.rst +27 -8
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/unicode_version.rst +15 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/pyproject.toml +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-tests38.in +1 -2
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-tests38.txt +4 -9
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-tests39.in +2 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-tests39.txt +7 -10
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-update.in +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-update.txt +8 -9
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/conftest.py +2 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_ambiguous.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_benchmarks.py +173 -6
- wcwidth-0.7.0/tests/test_clip.py +454 -0
- wcwidth-0.7.0/tests/test_clip_cjk_emoji.py +47 -0
- wcwidth-0.7.0/tests/test_clip_overtyping.py +159 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_core.py +32 -16
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_emojis.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_grapheme.py +1 -0
- wcwidth-0.7.0/tests/test_hyperlink.py +75 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_justify.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_sgr_state.py +1 -0
- wcwidth-0.7.0/tests/test_text_sizing.py +327 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_textwrap.py +39 -69
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_ucslevel.py +1 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/test_width.py +148 -22
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tox.ini +26 -28
- wcwidth-0.7.0/wcwidth/__init__.py +52 -0
- wcwidth-0.7.0/wcwidth/_clip.py +809 -0
- wcwidth-0.7.0/wcwidth/_constants.py +65 -0
- wcwidth-0.7.0/wcwidth/_wcswidth.py +150 -0
- wcwidth-0.7.0/wcwidth/_wcwidth.py +158 -0
- wcwidth-0.7.0/wcwidth/_width.py +339 -0
- wcwidth-0.7.0/wcwidth/align.py +136 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/bisearch.py +3 -2
- wcwidth-0.7.0/wcwidth/escape_sequences.py +194 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/grapheme.py +6 -6
- wcwidth-0.7.0/wcwidth/hyperlink.py +142 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/sgr_state.py +2 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/table_ambiguous.py +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/table_grapheme.py +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/table_mc.py +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/table_vs16.py +1 -1
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/table_wide.py +3 -3
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/table_zero.py +1 -1
- wcwidth-0.7.0/wcwidth/text_sizing.py +200 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/textwrap.py +35 -54
- wcwidth-0.7.0/wcwidth/wcwidth.py +69 -0
- wcwidth-0.6.0/bin/new-wide-by-version.py +0 -48
- wcwidth-0.6.0/tests/test_clip.py +0 -262
- wcwidth-0.6.0/wcwidth/__init__.py +0 -43
- wcwidth-0.6.0/wcwidth/escape_sequences.py +0 -69
- wcwidth-0.6.0/wcwidth/wcwidth.py +0 -1030
- {wcwidth-0.6.0 → wcwidth-0.7.0}/.gitignore +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/.pylintrc +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/LICENSE +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/README.rst +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/code_templates/unicode_version.rst.j2 +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/conf.py +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/docs/index.rst +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-develop.txt +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/requirements-docs.in +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/tests/__init__.py +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/control_codes.py +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/py.typed +0 -0
- {wcwidth-0.6.0 → wcwidth-0.7.0}/wcwidth/unicode_versions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wcwidth
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Measures the displayed width of unicode strings in a terminal
|
|
5
5
|
Project-URL: Homepage, https://github.com/jquast/wcwidth
|
|
6
6
|
Author-email: Jeff Quast <contact@jeffquast.com>
|
|
@@ -28,6 +28,7 @@ Classifier: Typing :: Typed
|
|
|
28
28
|
Requires-Python: >=3.8
|
|
29
29
|
Description-Content-Type: text/x-rst
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
|pypi_downloads| |codecov| |license|
|
|
32
33
|
|
|
33
34
|
============
|
|
@@ -65,33 +66,42 @@ Some examples of **incorrect results**:
|
|
|
65
66
|
Solution
|
|
66
67
|
--------
|
|
67
68
|
|
|
68
|
-
The lowest-level functions in this library are
|
|
69
|
-
`wcswidth(3)`_, which this library precisely copies by interface as `wcwidth()`_
|
|
70
|
-
These functions return -1 when C0 and C1 control codes are present.
|
|
69
|
+
The lowest-level functions in this library are derived from POSIX.1-2001 and POSIX.1-2008
|
|
70
|
+
`wcwidth(3)`_ and `wcswidth(3)`_, which this library precisely copies by interface as `wcwidth()`_
|
|
71
|
+
and `wcswidth()`_. These functions return -1 when C0 and C1 control codes are present.
|
|
71
72
|
|
|
72
73
|
An easy-to-use `width()`_ function is provided as a wrapper of `wcswidth()`_ that is also capable of
|
|
73
74
|
measuring most terminal control codes and sequences, like colors, bold, tabstops, and horizontal
|
|
74
75
|
cursor movement.
|
|
75
76
|
|
|
76
|
-
Text-justification is solved by the
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
Text-justification is solved by the sequence-aware functions `ljust()`_, `rjust()`_, `center()`_,
|
|
78
|
+
and the grapheme-aware function `wrap()`_, serving as drop-in replacements to python standard
|
|
79
|
+
functions.
|
|
80
|
+
|
|
81
|
+
The `clip()`_ function extracts substrings by their displayed column positions, and
|
|
82
|
+
`strip_sequences()`_ removes terminal escape sequences from text altogether.
|
|
79
83
|
|
|
80
84
|
The iterator functions `iter_graphemes()`_ and `iter_sequences()`_ allow for careful navigation of
|
|
81
|
-
grapheme and terminal control sequence boundaries
|
|
82
|
-
`grapheme_boundary_before()`_ are
|
|
83
|
-
|
|
84
|
-
terminal escape sequences from text altogether.
|
|
85
|
+
grapheme and terminal control sequence boundaries as required by editors or REPLs with cursor
|
|
86
|
+
control. `iter_graphemes_reverse()`_, and `grapheme_boundary_before()`_ are often necessary for
|
|
87
|
+
backward cursor control over complex unicode.
|
|
85
88
|
|
|
86
89
|
Discrepancies
|
|
87
90
|
-------------
|
|
88
91
|
|
|
89
|
-
You may find that support *varies* for complex unicode sequences or codepoints.
|
|
92
|
+
You may find that support *varies* for complex unicode sequences or codepoints. This library may be
|
|
93
|
+
considered to presume the terminal is enabled for DEC Private Mode 2027 ("Grapheme Clustering"), but
|
|
94
|
+
the specification does not fully describe varying unicode versions, feature levels, or details of
|
|
95
|
+
specific language support. This library does *not* support any alternate "legacy width"
|
|
96
|
+
measurement.
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
See `Grapheme Clusters and Terminal Emulators`_ and `terminal-unicode-core.tex`_, and `State of
|
|
99
|
+
Terminal Emulators in 2025`_ for more details on Mode 2027 and unicode-aware terminals.
|
|
100
|
+
|
|
101
|
+
The `jquast/ucs-detect`_ utility is used to gather and publish the results of compliance to our
|
|
102
|
+
standard for Wide character, Languages, grapheme clustering, complex or combining scripts, emojis,
|
|
103
|
+
zero-width joiner, variations, and regional indicator (flags) as a `General
|
|
104
|
+
Tabulated Summary`_ by terminal emulator software and version.
|
|
95
105
|
|
|
96
106
|
========
|
|
97
107
|
Overview
|
|
@@ -148,30 +158,61 @@ Use function `width()`_ to measure a string with improved handling of ``control_
|
|
|
148
158
|
>>> # same support as wcswidth(), eg. regional indicator flag:
|
|
149
159
|
>>> wcwidth.width('\U0001F1FF\U0001F1FC')
|
|
150
160
|
2
|
|
151
|
-
>>> # but also supports SGR colored text,
|
|
161
|
+
>>> # but also supports sequences, like SGR colored text, "WARN", followed by reset
|
|
152
162
|
>>> wcwidth.width('\x1b[38;2;255;150;100mWARN\x1b[0m')
|
|
153
163
|
4
|
|
154
|
-
>>> # tabs,
|
|
164
|
+
>>> # tabs are measured as though the string begins at a tabstop,
|
|
155
165
|
>>> wcwidth.width('\t', tabsize=4)
|
|
156
166
|
4
|
|
157
|
-
>>> # or,
|
|
158
|
-
>>> wcwidth.width('\t', control_codes='ignore')
|
|
159
|
-
0
|
|
160
|
-
>>> # "vertical" control characters are ignored
|
|
161
|
-
>>> wcwidth.width('\n')
|
|
167
|
+
>>> # or, all control characters can be ignored (including tab)
|
|
168
|
+
>>> wcwidth.width('\t\n\a\r', control_codes='ignore')
|
|
162
169
|
0
|
|
163
|
-
>>> #
|
|
170
|
+
>>> # sequences with "indeterminate" effects like Home + Clear are zero-width
|
|
164
171
|
>>> wcwidth.width('\x1b[H\x1b[2J')
|
|
165
172
|
0
|
|
173
|
+
>>> # horizontal cursor movements are parsed,
|
|
174
|
+
>>> wcwidth.width('hello\b\b\b\b\bworld')
|
|
175
|
+
5
|
|
176
|
+
>>> wcwidth.width('hello\x1b[5Dworld')
|
|
177
|
+
5
|
|
178
|
+
>>> # or ignored,
|
|
179
|
+
>>> wcwidth.width('hello\x1b[5Dworld', control_codes='ignore')
|
|
180
|
+
10
|
|
181
|
+
>>> # Measure width of text using kitty text sizing protocol (OSC 66),
|
|
182
|
+
>>> width('\x1b]66;w=2;XY\x07')
|
|
183
|
+
2
|
|
184
|
+
>>> # Scaled text sizing: each grapheme occupies 'scale' cells
|
|
185
|
+
>>> width('\x1b]66;s=2;ABC\x07')
|
|
186
|
+
6
|
|
187
|
+
|
|
188
|
+
Use ``control_codes='ignore'`` when the input is known not to contain any control characters or
|
|
189
|
+
terminal sequences for slightly improved performance. Note that TAB (``'\t'``) is a control
|
|
190
|
+
character and is also ignored, you may want to use `str.expandtabs()`_, first.
|
|
191
|
+
|
|
192
|
+
Use ``control_codes='strict'`` when input is known to contain some control sequences, such as
|
|
193
|
+
SGR color, bold, hyperlinks and cursor movement. Any sequence that cannot be accurately parsed,
|
|
194
|
+
such as clearing the screen, vertical, or absolute cursor movement will raise ``ValueError``:
|
|
195
|
+
|
|
196
|
+
.. code-block:: python
|
|
197
|
+
|
|
166
198
|
>>> # or, raise ValueError for "indeterminate" effects using control_codes='strict'
|
|
167
199
|
>>> wcwidth.width('\n', control_codes='strict')
|
|
168
200
|
Traceback (most recent call last):
|
|
169
201
|
...
|
|
170
202
|
ValueError: Vertical movement character 0xa at position 0
|
|
171
203
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
204
|
+
|
|
205
|
+
>>> wcwidth.width('\x1b[H\x1b[2J', control_codes='strict')
|
|
206
|
+
Traceback (most recent call last):
|
|
207
|
+
...
|
|
208
|
+
ValueError: Indeterminate cursor sequence at position 0, '\x1b[H'
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
>>> # cursor left movement beyond string start raises in strict mode,
|
|
212
|
+
>>> wcwidth.width('a\x1b[5Da', control_codes='strict')
|
|
213
|
+
Traceback (most recent call last):
|
|
214
|
+
...
|
|
215
|
+
ValueError: Cursor left movement at position 1 would move 5 cells left from column 1, exceeding string start
|
|
175
216
|
|
|
176
217
|
iter_sequences()
|
|
177
218
|
----------------
|
|
@@ -290,9 +331,29 @@ Use `clip()`_ to extract a substring by column positions, preserving terminal se
|
|
|
290
331
|
>>> clip('\x1b[1;31mHello world\x1b[0m', 6, 11)
|
|
291
332
|
'\x1b[1;31mworld\x1b[0m'
|
|
292
333
|
|
|
293
|
-
>>> # Disable SGR propagation to preserve
|
|
294
|
-
>>> clip('\x1b[31m中文\x1b[
|
|
295
|
-
'\x1b[31m中 \x1b[
|
|
334
|
+
>>> # Disable SGR propagation to preserve sequence order outside of clip boundary
|
|
335
|
+
>>> clip('\x1b[31m中文\x1b[32m', 0, 3, propagate_sgr=False)
|
|
336
|
+
'\x1b[31m中 \x1b[32m'
|
|
337
|
+
|
|
338
|
+
>>> # Cursor-left overwrites previous text (painter's algorithm)
|
|
339
|
+
>>> clip('hello\x1b[2DXY', 0, 5)
|
|
340
|
+
'helXY'
|
|
341
|
+
>>> # Carriage return resets to column 0, overwriting earlier cells
|
|
342
|
+
>>> clip('abc\rXY', 0, 5)
|
|
343
|
+
'XYc'
|
|
344
|
+
|
|
345
|
+
>>> # even OSC 8 hyperlink text may be clipped, 'Click This link' -> 'is link' !
|
|
346
|
+
>>> clip('\x1b]8;;http://example.com\x07Click This link\x1b]8;;\x07', 8, 15)
|
|
347
|
+
'\x1b]8;;http://example.com\x07is link\x1b]8;;\x07'
|
|
348
|
+
|
|
349
|
+
>>> # and OSC 66 kitty text sizing, supporting width and scale, 'Look' -> '...ook'
|
|
350
|
+
>>> clip('\x1b]66;w=4:s=4;Look\x07', 1, 16, fillchar='.')
|
|
351
|
+
'...\x1b]66;s=4:w=3;ook\x07'
|
|
352
|
+
|
|
353
|
+
Use ``overtyping=False`` when the input is known not to contain any cursor movement characters
|
|
354
|
+
(``\b``, ``\r``, ``CSI C``, ``CSI D``, ``CSI G``) for improved performance. When
|
|
355
|
+
``overtyping=None`` (default), a slower "Painter's algorithm" may be used after testing for the
|
|
356
|
+
presence of these characters. ``overtyping`` has no effect when ``control_codes='ignore'``.
|
|
296
357
|
|
|
297
358
|
strip_sequences()
|
|
298
359
|
-----------------
|
|
@@ -336,7 +397,7 @@ mode is to display an ambiguous character surrounded by a pair of Cursor Positio
|
|
|
336
397
|
queries with a terminal in cooked or raw mode, and to parse the responses for their ``(y, x)``
|
|
337
398
|
locations and measure the difference ``x``.
|
|
338
399
|
|
|
339
|
-
This code should also be careful check whether it is attached to a terminal and be careful of
|
|
400
|
+
This code should also be careful to check whether it is attached to a terminal and be careful of
|
|
340
401
|
possible timeout, slow network, or non-response when working with "dumb terminals" like a CI build.
|
|
341
402
|
|
|
342
403
|
`jquast/blessed`_ library provides such a helping `Terminal.detect_ambiguous_width()`_ method:
|
|
@@ -429,9 +490,18 @@ This library is used in:
|
|
|
429
490
|
- `jquast/blessed`_: a thin, practical wrapper around terminal capabilities in
|
|
430
491
|
Python.
|
|
431
492
|
|
|
493
|
+
- `jquast/telix`_: A Modern telnet client especially designed for BBSs and MUDs.
|
|
494
|
+
|
|
432
495
|
- `prompt-toolkit/python-prompt-toolkit`_: a Library for building powerful
|
|
433
496
|
interactive command lines in Python.
|
|
434
497
|
|
|
498
|
+
- `urwid/urwid`_: Console user interface library for Python
|
|
499
|
+
|
|
500
|
+
- `prettytable/prettytable`_: Display tabular data in a visually appealing ASCII table format
|
|
501
|
+
|
|
502
|
+
- `leviathan0992/Pylsy`_: Pylsy is a simple python library draw tables in the Terminal. Just two
|
|
503
|
+
lines of code.
|
|
504
|
+
|
|
435
505
|
- `dbcli/pgcli`_: Postgres CLI with autocompletion and syntax highlighting.
|
|
436
506
|
|
|
437
507
|
- `thomasballinger/curtsies`_: a Curses-like terminal wrapper with a display
|
|
@@ -448,8 +518,8 @@ This library is used in:
|
|
|
448
518
|
- `nbedos/termtosvg`_: Terminal recorder that renders sessions as SVG
|
|
449
519
|
animations.
|
|
450
520
|
|
|
451
|
-
- `peterbrittain/asciimatics`_:
|
|
452
|
-
UIs
|
|
521
|
+
- `peterbrittain/asciimatics`_: A cross platform package to do curses-like operations, plus higher
|
|
522
|
+
level APIs and widgets to create text UIs and ASCII art animations
|
|
453
523
|
|
|
454
524
|
- `python-cmd2/cmd2`_: A tool for building interactive command line apps
|
|
455
525
|
|
|
@@ -469,6 +539,10 @@ Other Languages
|
|
|
469
539
|
There are similar implementations of the `wcwidth()`_ and `wcswidth()`_ functions in other
|
|
470
540
|
languages.
|
|
471
541
|
|
|
542
|
+
- `contour-terminal/libunicode`_: C++20
|
|
543
|
+
- `ridiculousfish/widecharwidth`_: Python
|
|
544
|
+
- `termux/wcwidth`_: C
|
|
545
|
+
- `powerman/wcwidth-icons`_: C
|
|
472
546
|
- `timoxley/wcwidth`_: JavaScript
|
|
473
547
|
- `janlelis/unicode-display_width`_: Ruby
|
|
474
548
|
- `alecrabbit/php-wcwidth`_: PHP
|
|
@@ -478,6 +552,9 @@ languages.
|
|
|
478
552
|
- `grepsuzette/wcwidth`_: Haxe
|
|
479
553
|
- `aperezdc/lua-wcwidth`_: Lua
|
|
480
554
|
- `joachimschmidt557/zig-wcwidth`_: Zig
|
|
555
|
+
- `mycoboco/wcwidth.js`_: JavaScript
|
|
556
|
+
- `ainame/swift-displaywidth`_: Swift
|
|
557
|
+
- `pmonks/clj-wcwidth`_: Clojure
|
|
481
558
|
- `fumiyas/wcwidth-cjk`_: `LD_PRELOAD` override
|
|
482
559
|
- `joshuarubin/wcwidth9`_: Unicode version 9 in C
|
|
483
560
|
- `spectreconsole/wcwidth`_: C#
|
|
@@ -486,6 +563,15 @@ languages.
|
|
|
486
563
|
History
|
|
487
564
|
=======
|
|
488
565
|
|
|
566
|
+
0.7.0 *2026-05-02*
|
|
567
|
+
* **New** support for `kitty text sizing protocol`_ (OSC 66) in `width()`_ and `clip()`_.
|
|
568
|
+
* **New** `clip()`_ parameter ``control_codes='parse'``, ``'ignore'``, and ``'strict'``. `clip()`_
|
|
569
|
+
is now able to clip OSC 8 hyperlinks and OSC 66 text sizing sequences.
|
|
570
|
+
* **Improved** `clip()`_ and `width()`_ to support horizontal cursor sequences (``cub``, ``cuf``,
|
|
571
|
+
``hpa``). Cursor-left (``cub``) or backspace (``\b``) now overwrites text. ``column_address``
|
|
572
|
+
(``hpa``) and carriage return (``\r``) are now parsed, and more values conditionally raise
|
|
573
|
+
``ValueError`` when ``control_codes='strict'``.
|
|
574
|
+
|
|
489
575
|
0.6.0 *2026-02-06*
|
|
490
576
|
* **New** Parameters ``expand_tabs``, ``replace_whitespace``, ``fix_sentence_endings``,
|
|
491
577
|
``drop_whitespace``, ``max_lines``, and ``placeholder`` for `wrap()`_, completing stdlib
|
|
@@ -711,6 +797,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
711
797
|
.. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
|
|
712
798
|
.. _`Issue #190`: https://github.com/jquast/wcwidth/issues/190
|
|
713
799
|
.. _`jquast/blessed`: https://github.com/jquast/blessed
|
|
800
|
+
.. _`jquast/telix`: https://github.com/jquast/telix
|
|
714
801
|
.. _`selectel/pyte`: https://github.com/selectel/pyte
|
|
715
802
|
.. _`thomasballinger/curtsies`: https://github.com/thomasballinger/curtsies
|
|
716
803
|
.. _`dbcli/pgcli`: https://github.com/dbcli/pgcli
|
|
@@ -735,10 +822,20 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
735
822
|
.. _`fumiyas/wcwidth-cjk`: https://github.com/fumiyas/wcwidth-cjk
|
|
736
823
|
.. _`joshuarubin/wcwidth9`: https://github.com/joshuarubin/wcwidth9
|
|
737
824
|
.. _`spectreconsole/wcwidth`: https://github.com/spectreconsole/wcwidth
|
|
825
|
+
.. _`contour-terminal/libunicode`: https://github.com/contour-terminal/libunicode
|
|
826
|
+
.. _`ridiculousfish/widecharwidth`: https://github.com/ridiculousfish/widecharwidth
|
|
827
|
+
.. _`termux/wcwidth`: https://github.com/termux/wcwidth
|
|
828
|
+
.. _`powerman/wcwidth-icons`: https://github.com/powerman/wcwidth-icons
|
|
829
|
+
.. _`mycoboco/wcwidth.js`: https://github.com/mycoboco/wcwidth.js
|
|
830
|
+
.. _`ainame/swift-displaywidth`: https://github.com/ainame/swift-displaywidth
|
|
831
|
+
.. _`pmonks/clj-wcwidth`: https://github.com/pmonks/clj-wcwidth
|
|
738
832
|
.. _`python-cmd2/cmd2`: https://github.com/python-cmd2/cmd2
|
|
739
833
|
.. _`stratis-storage/stratis-cli`: https://github.com/stratis-storage/stratis-cli
|
|
740
834
|
.. _`ihabunek/toot`: https://github.com/ihabunek/toot
|
|
741
835
|
.. _`saulpw/visidata`: https://github.com/saulpw/visidata
|
|
836
|
+
.. _`urwid/urwid`: https://github.com/urwid/urwid
|
|
837
|
+
.. _`prettytable/prettytable`: https://github.com/urwid/urwid
|
|
838
|
+
.. _`leviathan0992/Pylsy`: https://github.com/leviathan0992/Pylsy
|
|
742
839
|
.. _`pip-tools`: https://pip-tools.readthedocs.io/
|
|
743
840
|
.. _`sphinx`: https://www.sphinx-doc.org/
|
|
744
841
|
.. _`textwrap.wrap()`: https://docs.python.org/3/library/textwrap.html#textwrap.wrap
|
|
@@ -760,11 +857,17 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
|
|
|
760
857
|
.. _`clip()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.clip
|
|
761
858
|
.. _`strip_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.strip_sequences
|
|
762
859
|
.. _`propagate_sgr()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.propagate_sgr
|
|
860
|
+
.. _`TextSizing`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.TextSizing
|
|
861
|
+
.. _`TextSizingParams`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.TextSizingParams
|
|
763
862
|
.. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
|
|
764
863
|
.. _`list_versions()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_versions
|
|
765
864
|
.. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
|
|
766
865
|
.. _`Terminal.detect_ambiguous_width()`: https://blessed.readthedocs.io/en/latest/api/terminal.html#blessed.terminal.Terminal.detect_ambiguous_width
|
|
767
866
|
.. _`parity padding`: https://jazcap53.github.io/pythons-eccentric-strcenter.html
|
|
867
|
+
.. _`kitty text sizing protocol`: https://sw.kovidgoyal.net/kitty/text-sizing-protocol/
|
|
868
|
+
.. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
|
|
869
|
+
.. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
|
|
870
|
+
.. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
|
|
768
871
|
.. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi
|
|
769
872
|
:alt: Downloads
|
|
770
873
|
:target: https://pypi.org/project/wcwidth/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Show terminal escape sequences in a file or stdin as repr() literals."""
|
|
3
|
+
# std imports
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
# local
|
|
7
|
+
from wcwidth import iter_sequences
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _read_input():
|
|
11
|
+
# some care is taken regarding '\r\n' because this program is meant to show
|
|
12
|
+
# raw terminal data to terminals that are *not* in raw mode, so different
|
|
13
|
+
# interpretation of '\r\n' is needed.
|
|
14
|
+
if len(sys.argv) > 1:
|
|
15
|
+
with open(sys.argv[1], "rb") as f:
|
|
16
|
+
return f.read().replace(b"\r\n", b"\n").decode()
|
|
17
|
+
return sys.stdin.buffer.read().replace(b"\r\n", b"\n").decode()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
for line in _read_input().split("\n"):
|
|
21
|
+
for item, is_seq in iter_sequences(line):
|
|
22
|
+
print(repr(item) if is_seq else item.replace("\r", repr("\r")), end="")
|
|
23
|
+
print("", flush=True)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Strip terminal escape sequences from a file or stdin."""
|
|
3
|
+
# std imports
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
# local
|
|
7
|
+
from wcwidth import strip_sequences
|
|
8
|
+
|
|
9
|
+
# some care is taken regarding '\r\n' because this program is meant to show
|
|
10
|
+
# raw terminal data to terminals that are *not* in raw mode, so different
|
|
11
|
+
# interpretation of '\r\n' is needed.
|
|
12
|
+
|
|
13
|
+
if len(sys.argv) > 1:
|
|
14
|
+
with open(sys.argv[1], "rb") as f:
|
|
15
|
+
text = f.read().replace(b"\r\n", b"\n").replace(b"\r", b"").decode()
|
|
16
|
+
else:
|
|
17
|
+
text = sys.stdin.buffer.read().replace(b"\r\n", b"\n").replace(b"\r", b"").decode()
|
|
18
|
+
sys.stdout.write(strip_sequences(text))
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
"""
|
|
3
|
-
Update the Unicode code tables for wcwidth.
|
|
3
|
+
Update the Unicode code tables for wcwidth.
|
|
4
4
|
|
|
5
|
-
This is typically executed through tox,
|
|
5
|
+
This is code generation using jinja2. This is typically executed through tox,
|
|
6
6
|
|
|
7
7
|
$ tox -e update
|
|
8
8
|
|
|
9
9
|
https://github.com/jquast/wcwidth
|
|
10
10
|
"""
|
|
11
|
+
|
|
11
12
|
from __future__ import annotations
|
|
12
13
|
|
|
13
14
|
# std imports
|
|
@@ -24,7 +25,7 @@ import unicodedata
|
|
|
24
25
|
from pathlib import Path
|
|
25
26
|
from dataclasses import field, fields, dataclass
|
|
26
27
|
|
|
27
|
-
from typing import Any, Mapping, Iterable, Iterator, Sequence, Collection
|
|
28
|
+
from typing import Any, Mapping, Iterable, Iterator, Optional, Sequence, Collection
|
|
28
29
|
|
|
29
30
|
try:
|
|
30
31
|
from typing import Self
|
|
@@ -108,9 +109,10 @@ def _bisearch(ucs, table):
|
|
|
108
109
|
@dataclass(order=True, frozen=True)
|
|
109
110
|
class UnicodeVersion:
|
|
110
111
|
"""A class for comparable unicode version."""
|
|
112
|
+
|
|
111
113
|
major: int
|
|
112
114
|
minor: int
|
|
113
|
-
micro: int
|
|
115
|
+
micro: Optional[int]
|
|
114
116
|
|
|
115
117
|
@classmethod
|
|
116
118
|
def parse(cls, version_str: str) -> UnicodeVersion:
|
|
@@ -138,7 +140,8 @@ class UnicodeVersion:
|
|
|
138
140
|
@dataclass(frozen=True)
|
|
139
141
|
class TableEntry:
|
|
140
142
|
"""An entry of a unicode table."""
|
|
141
|
-
|
|
143
|
+
|
|
144
|
+
code_range: Optional[tuple[int, int]]
|
|
142
145
|
properties: tuple[str, ...]
|
|
143
146
|
comment: str
|
|
144
147
|
|
|
@@ -255,6 +258,7 @@ class UnicodeTableRenderCtx(RenderContext):
|
|
|
255
258
|
@dataclass
|
|
256
259
|
class RenderDefinition:
|
|
257
260
|
"""Base class, do not instantiate it directly."""
|
|
261
|
+
|
|
258
262
|
jinja_filename: str
|
|
259
263
|
output_filename: str
|
|
260
264
|
render_context: RenderContext
|
|
@@ -330,6 +334,7 @@ class UnicodeTableRenderDef(RenderDefinition):
|
|
|
330
334
|
@dataclass(frozen=True)
|
|
331
335
|
class GraphemeTableRenderCtx(RenderContext):
|
|
332
336
|
"""Render context for grapheme tables (latest version only)."""
|
|
337
|
+
|
|
333
338
|
unicode_version: str
|
|
334
339
|
tables: Mapping[str, TableDef]
|
|
335
340
|
|
|
@@ -880,7 +885,6 @@ def fetch_table_grapheme_data() -> GraphemeTableRenderCtx:
|
|
|
880
885
|
tables.update(parse_indic_syllabic_category(
|
|
881
886
|
UnicodeDataFile.IndicSyllabicCategory(latest_version)
|
|
882
887
|
))
|
|
883
|
-
|
|
884
888
|
return GraphemeTableRenderCtx(str(latest_version), tables)
|
|
885
889
|
|
|
886
890
|
|
|
@@ -895,6 +899,7 @@ class UnicodeDataFile:
|
|
|
895
899
|
TestEmojiVariationSequences, these files should be forcefully re-fetched CLI argument '--no-
|
|
896
900
|
check-last-modified'.
|
|
897
901
|
"""
|
|
902
|
+
|
|
898
903
|
URL_DERIVED_AGE = 'https://www.unicode.org/Public/UCD/latest/ucd/DerivedAge.txt'
|
|
899
904
|
URL_EASTASIAN_WIDTH = 'https://www.unicode.org/Public/{version}/ucd/EastAsianWidth.txt'
|
|
900
905
|
URL_DERIVED_CATEGORY = 'https://www.unicode.org/Public/{version}/ucd/extracted/DerivedGeneralCategory.txt'
|
|
@@ -376,6 +376,7 @@ class Screen:
|
|
|
376
376
|
|
|
377
377
|
class Pager:
|
|
378
378
|
"""A less(1)-like browser for browsing unicode characters."""
|
|
379
|
+
|
|
379
380
|
# pylint: disable=too-many-instance-attributes
|
|
380
381
|
|
|
381
382
|
#: screen state for next draw method(s).
|
|
@@ -520,14 +521,13 @@ class Pager:
|
|
|
520
521
|
"""
|
|
521
522
|
Pager entry point.
|
|
522
523
|
|
|
523
|
-
In interactive mode (terminal is a tty), run until
|
|
524
|
-
|
|
525
|
-
non-interactive mode, exit after displaying all unicode points.
|
|
524
|
+
In interactive mode (terminal is a tty), run until ``process_keystroke()`` detects quit
|
|
525
|
+
keystroke ('q'). In non-interactive mode, exit after displaying all unicode points.
|
|
526
526
|
|
|
527
527
|
:param writer: callable writes to output stream, receiving unicode.
|
|
528
528
|
:type writer: callable
|
|
529
|
-
:param reader: callable reads keystrokes from input stream, sending
|
|
530
|
-
|
|
529
|
+
:param reader: callable reads keystrokes from input stream, sending instance of
|
|
530
|
+
blessed.keyboard.Keystroke.
|
|
531
531
|
:type reader: callable
|
|
532
532
|
"""
|
|
533
533
|
self.initialize_page_data()
|
|
@@ -720,8 +720,8 @@ class Pager:
|
|
|
720
720
|
"""
|
|
721
721
|
Conditionally redraw screen when ``dirty`` attribute is valued REFRESH.
|
|
722
722
|
|
|
723
|
-
When Pager attribute ``dirty`` is ``STATE_REFRESH``, cursor is moved
|
|
724
|
-
|
|
723
|
+
When Pager attribute ``dirty`` is ``STATE_REFRESH``, cursor is moved to (0,0), screen is
|
|
724
|
+
cleared, and heading is displayed.
|
|
725
725
|
|
|
726
726
|
:param callable writer: callable writes to output stream, receiving unicode.
|
|
727
727
|
:return: True if class attribute ``dirty`` is ``STATE_REFRESH``.
|
|
@@ -787,8 +787,8 @@ class Pager:
|
|
|
787
787
|
"""
|
|
788
788
|
Generator yields text to be displayed for the current unicode pageview.
|
|
789
789
|
|
|
790
|
-
:param list[(unicode, unicode)] data: The current page's data as tuple
|
|
791
|
-
|
|
790
|
+
:param list[(unicode, unicode)] data: The current page's data as tuple of ``(ucs,
|
|
791
|
+
name)``.
|
|
792
792
|
:returns: generator for full-page text for display
|
|
793
793
|
"""
|
|
794
794
|
if self.term.is_a_tty:
|
|
@@ -13,6 +13,7 @@ platforms -- usually conforming only to unicode specification 1.0 or 2.0.
|
|
|
13
13
|
This program accepts one optional command-line argument, the unicode version
|
|
14
14
|
level for our library to use when comparing to libc.
|
|
15
15
|
"""
|
|
16
|
+
|
|
16
17
|
# pylint: disable=C0103
|
|
17
18
|
# Invalid module name "wcwidth-libc-comparator"
|
|
18
19
|
|
|
@@ -4,7 +4,7 @@ Exports grapheme cluster break property tables for Unicode version {{ unicode_ve
|
|
|
4
4
|
This module provides lookup tables for Unicode grapheme cluster break properties as defined in UAX
|
|
5
5
|
#29: Unicode Text Segmentation.
|
|
6
6
|
|
|
7
|
-
This code generated by
|
|
7
|
+
This code generated by python wcwidth project.
|
|
8
8
|
"""
|
|
9
9
|
# pylint: disable=duplicate-code
|
|
10
10
|
{%- for var_name, table_def in tables.items() %}
|
|
@@ -36,4 +36,12 @@ requirements.txt or equivalent. Their signatures will never change.
|
|
|
36
36
|
|
|
37
37
|
.. autofunction:: wcwidth.list_versions
|
|
38
38
|
|
|
39
|
+
.. autofunction:: wcwidth.Hyperlink
|
|
40
|
+
|
|
41
|
+
.. autofunction:: wcwidth.HyperlinkParams
|
|
42
|
+
|
|
43
|
+
.. autofunction:: wcwidth.TextSizing
|
|
44
|
+
|
|
45
|
+
.. autofunction:: wcwidth.TextSizingParams
|
|
46
|
+
|
|
39
47
|
.. _SEMVER: https://semver.org
|