wcwidth 0.7.0__tar.gz → 0.8.1__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.
Files changed (152) hide show
  1. {wcwidth-0.7.0 → wcwidth-0.8.1}/.pylintrc +2 -0
  2. {wcwidth-0.7.0 → wcwidth-0.8.1}/PKG-INFO +179 -26
  3. {wcwidth-0.7.0 → wcwidth-0.8.1}/bin/update-tables.py +610 -51
  4. wcwidth-0.8.1/code_templates/grapheme_override_per_terminal.py.j2 +13 -0
  5. wcwidth-0.8.1/code_templates/grapheme_override_table.py.j2 +18 -0
  6. wcwidth-0.8.1/code_templates/grapheme_registry.py.j2 +13 -0
  7. wcwidth-0.8.1/code_templates/table_overrides.py.j2 +40 -0
  8. wcwidth-0.8.1/code_templates/term_programs.py.j2 +22 -0
  9. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/api.rst +6 -0
  10. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/intro.rst +177 -25
  11. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/requirements.txt +2 -2
  12. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/specs.rst +40 -24
  13. {wcwidth-0.7.0 → wcwidth-0.8.1}/pyproject.toml +5 -1
  14. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-tests39.txt +3 -4
  15. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-update.in +1 -0
  16. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-update.txt +7 -4
  17. wcwidth-0.8.1/tests/conftest.py +38 -0
  18. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_benchmarks.py +84 -2
  19. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_clip.py +2 -2
  20. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_core.py +48 -11
  21. wcwidth-0.8.1/tests/test_term_overrides.py +590 -0
  22. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_text_sizing.py +2 -1
  23. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_width.py +6 -0
  24. {wcwidth-0.7.0 → wcwidth-0.8.1}/tox.ini +7 -3
  25. wcwidth-0.8.1/ucs-detect/data/AbsoluteTelnetSSH.yaml +789 -0
  26. wcwidth-0.8.1/ucs-detect/data/_linuxfbdev.yaml +50 -0
  27. wcwidth-0.8.1/ucs-detect/data/_syncterm.yaml +38 -0
  28. wcwidth-0.8.1/ucs-detect/data/alacritty.yaml +8821 -0
  29. wcwidth-0.8.1/ucs-detect/data/bobcat.yaml +10804 -0
  30. wcwidth-0.8.1/ucs-detect/data/conemu.exe.yaml +27608 -0
  31. wcwidth-0.8.1/ucs-detect/data/contour.yaml +21578 -0
  32. wcwidth-0.8.1/ucs-detect/data/coolretroterm.yaml +10734 -0
  33. wcwidth-0.8.1/ucs-detect/data/emoji-test-latest.txt +5518 -0
  34. wcwidth-0.8.1/ucs-detect/data/emoji-variation-sequences-latest.txt +757 -0
  35. wcwidth-0.8.1/ucs-detect/data/emoji-zwj-sequences-latest.txt +1675 -0
  36. wcwidth-0.8.1/ucs-detect/data/extraterm.yaml +33248 -0
  37. wcwidth-0.8.1/ucs-detect/data/foot.yaml +8258 -0
  38. wcwidth-0.8.1/ucs-detect/data/ghostty.yaml +1010 -0
  39. wcwidth-0.8.1/ucs-detect/data/gnometerminal.yaml +16247 -0
  40. wcwidth-0.8.1/ucs-detect/data/hyper.yaml +12553 -0
  41. wcwidth-0.8.1/ucs-detect/data/iterm2.yaml +6564 -0
  42. wcwidth-0.8.1/ucs-detect/data/kitty.yaml +15643 -0
  43. wcwidth-0.8.1/ucs-detect/data/konsole.yaml +7624 -0
  44. wcwidth-0.8.1/ucs-detect/data/libvterm.yaml +13536 -0
  45. wcwidth-0.8.1/ucs-detect/data/lxterminal.yaml +12959 -0
  46. wcwidth-0.8.1/ucs-detect/data/mintty.yaml +26246 -0
  47. wcwidth-0.8.1/ucs-detect/data/mlterm.yaml +17478 -0
  48. wcwidth-0.8.1/ucs-detect/data/putty.yaml +10682 -0
  49. wcwidth-0.8.1/ucs-detect/data/qterminal.yaml +9153 -0
  50. wcwidth-0.8.1/ucs-detect/data/rio.yaml +11346 -0
  51. wcwidth-0.8.1/ucs-detect/data/rxvtunicode.yaml +10736 -0
  52. wcwidth-0.8.1/ucs-detect/data/screen.yaml +19446 -0
  53. wcwidth-0.8.1/ucs-detect/data/securecrt.yaml +16450 -0
  54. wcwidth-0.8.1/ucs-detect/data/st.yaml +10669 -0
  55. wcwidth-0.8.1/ucs-detect/data/tabby.yaml +13641 -0
  56. wcwidth-0.8.1/ucs-detect/data/teraterm.yaml +6684 -0
  57. wcwidth-0.8.1/ucs-detect/data/terminalapp.yaml +10901 -0
  58. wcwidth-0.8.1/ucs-detect/data/terminalexe.yaml +3078 -0
  59. wcwidth-0.8.1/ucs-detect/data/terminator.yaml +15308 -0
  60. wcwidth-0.8.1/ucs-detect/data/terminology.yaml +31854 -0
  61. wcwidth-0.8.1/ucs-detect/data/tmux.yaml +8371 -0
  62. wcwidth-0.8.1/ucs-detect/data/vscodeterminal.yaml +11720 -0
  63. wcwidth-0.8.1/ucs-detect/data/warp.yaml +13297 -0
  64. wcwidth-0.8.1/ucs-detect/data/westonterminal.yaml +26416 -0
  65. wcwidth-0.8.1/ucs-detect/data/wezterm.yaml +10571 -0
  66. wcwidth-0.8.1/ucs-detect/data/xfce4terminal.yaml +13040 -0
  67. wcwidth-0.8.1/ucs-detect/data/xterm.yaml +10794 -0
  68. wcwidth-0.8.1/ucs-detect/data/zellij.yaml +13479 -0
  69. wcwidth-0.8.1/ucs-detect/data/zutty.yaml +10704 -0
  70. wcwidth-0.8.1/wcwidth/__init__.py +81 -0
  71. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/_clip.py +31 -5
  72. wcwidth-0.8.1/wcwidth/_constants.py +164 -0
  73. wcwidth-0.8.1/wcwidth/_wcswidth.py +439 -0
  74. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/_wcwidth.py +4 -0
  75. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/_width.py +232 -56
  76. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/align.py +32 -3
  77. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/grapheme.py +67 -2
  78. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/table_grapheme.py +53 -0
  79. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/__init__.py +37 -0
  80. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_27e0693f.py +2677 -0
  81. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_3d4826b8.py +6462 -0
  82. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_45d92e98.py +1969 -0
  83. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_4cdf59ce.py +42 -0
  84. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_50bf0759.py +2571 -0
  85. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_529fbb4a.py +1799 -0
  86. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_5bfac390.py +2571 -0
  87. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_813fee16.py +2570 -0
  88. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_8589765c.py +1310 -0
  89. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_8f94b404.py +2581 -0
  90. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_970dbe10.py +2839 -0
  91. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_c0a2cdbf.py +3617 -0
  92. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_c2157f7e.py +1153 -0
  93. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_c3db41c0.py +3401 -0
  94. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_da9ceb0a.py +1910 -0
  95. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_e08bd75e.py +2641 -0
  96. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_e22030f3.py +6396 -0
  97. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_fcc05a0f.py +6648 -0
  98. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_known_fd9d4c44.py +3671 -0
  99. wcwidth-0.8.1/wcwidth/table_grapheme_overrides/_registry.py +32 -0
  100. wcwidth-0.8.1/wcwidth/table_overrides.py +853 -0
  101. wcwidth-0.8.1/wcwidth/table_term_programs.py +49 -0
  102. wcwidth-0.8.1/wcwidth/table_vs15.py +104 -0
  103. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/text_sizing.py +3 -1
  104. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/textwrap.py +20 -1
  105. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/unicode_versions.py +1 -1
  106. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/wcwidth.py +28 -9
  107. wcwidth-0.7.0/tests/conftest.py +0 -17
  108. wcwidth-0.7.0/wcwidth/__init__.py +0 -52
  109. wcwidth-0.7.0/wcwidth/_constants.py +0 -65
  110. wcwidth-0.7.0/wcwidth/_wcswidth.py +0 -150
  111. {wcwidth-0.7.0 → wcwidth-0.8.1}/.gitignore +0 -0
  112. {wcwidth-0.7.0 → wcwidth-0.8.1}/LICENSE +0 -0
  113. {wcwidth-0.7.0 → wcwidth-0.8.1}/README.rst +0 -0
  114. {wcwidth-0.7.0 → wcwidth-0.8.1}/bin/show-sequences +0 -0
  115. {wcwidth-0.7.0 → wcwidth-0.8.1}/bin/strip-sequences +0 -0
  116. {wcwidth-0.7.0 → wcwidth-0.8.1}/bin/verify-table-integrity.py +0 -0
  117. {wcwidth-0.7.0 → wcwidth-0.8.1}/bin/wcwidth-browser.py +0 -0
  118. {wcwidth-0.7.0 → wcwidth-0.8.1}/bin/wcwidth-libc-comparator.py +0 -0
  119. {wcwidth-0.7.0 → wcwidth-0.8.1}/code_templates/grapheme_table.py.j2 +0 -0
  120. {wcwidth-0.7.0 → wcwidth-0.8.1}/code_templates/python_table.py.j2 +0 -0
  121. {wcwidth-0.7.0 → wcwidth-0.8.1}/code_templates/unicode_version.rst.j2 +0 -0
  122. {wcwidth-0.7.0 → wcwidth-0.8.1}/code_templates/unicode_versions.py.j2 +0 -0
  123. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/conf.py +0 -0
  124. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/index.rst +0 -0
  125. {wcwidth-0.7.0 → wcwidth-0.8.1}/docs/unicode_version.rst +0 -0
  126. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-develop.txt +0 -0
  127. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-docs.in +0 -0
  128. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-tests38.in +0 -0
  129. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-tests38.txt +0 -0
  130. {wcwidth-0.7.0 → wcwidth-0.8.1}/requirements-tests39.in +0 -0
  131. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/__init__.py +0 -0
  132. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_ambiguous.py +0 -0
  133. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_clip_cjk_emoji.py +0 -0
  134. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_clip_overtyping.py +0 -0
  135. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_emojis.py +0 -0
  136. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_grapheme.py +0 -0
  137. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_hyperlink.py +0 -0
  138. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_justify.py +0 -0
  139. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_sgr_state.py +0 -0
  140. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_textwrap.py +0 -0
  141. {wcwidth-0.7.0 → wcwidth-0.8.1}/tests/test_ucslevel.py +0 -0
  142. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/bisearch.py +0 -0
  143. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/control_codes.py +0 -0
  144. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/escape_sequences.py +0 -0
  145. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/hyperlink.py +0 -0
  146. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/py.typed +0 -0
  147. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/sgr_state.py +0 -0
  148. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/table_ambiguous.py +0 -0
  149. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/table_mc.py +0 -0
  150. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/table_vs16.py +0 -0
  151. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/table_wide.py +0 -0
  152. {wcwidth-0.7.0 → wcwidth-0.8.1}/wcwidth/table_zero.py +0 -0
@@ -26,6 +26,8 @@ disable=
26
26
  too-many-boolean-expressions,
27
27
  redundant-u-string-prefix,
28
28
  consider-using-f-string,
29
+ consider-using-max-builtin,
30
+ consider-using-min-builtin,
29
31
 
30
32
  [FORMAT]
31
33
  max-line-length: 100
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcwidth
3
- Version: 0.7.0
3
+ Version: 0.8.1
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>
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Python :: 3.15
23
24
  Classifier: Topic :: Software Development :: Internationalization
24
25
  Classifier: Topic :: Software Development :: Libraries
25
26
  Classifier: Topic :: Software Development :: Localization
@@ -72,7 +73,8 @@ and `wcswidth()`_. These functions return -1 when C0 and C1 control codes are p
72
73
 
73
74
  An easy-to-use `width()`_ function is provided as a wrapper of `wcswidth()`_ that is also capable of
74
75
  measuring most terminal control codes and sequences, like colors, bold, tabstops, and horizontal
75
- cursor movement.
76
+ cursor movement. `width()`_ argument ``term_program`` may provide more accurate terminal measurement
77
+ Corrections_ as a wrapper of `wcstwidth()`_.
76
78
 
77
79
  Text-justification is solved by the sequence-aware functions `ljust()`_, `rjust()`_, `center()`_,
78
80
  and the grapheme-aware function `wrap()`_, serving as drop-in replacements to python standard
@@ -83,25 +85,31 @@ The `clip()`_ function extracts substrings by their displayed column positions,
83
85
 
84
86
  The iterator functions `iter_graphemes()`_ and `iter_sequences()`_ allow for careful navigation of
85
87
  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.
88
+ control. `iter_graphemes_reverse()`_ and `grapheme_boundary_before()`_ are necessary for backward
89
+ cursor control over complex unicode.
88
90
 
89
91
  Discrepancies
90
92
  -------------
91
93
 
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.
94
+ You may find that support *varies* for complex unicode sequences or codepoints.
97
95
 
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.
96
+ This library may be considered to presume the terminal is enabled for DEC Private Mode 2027
97
+ ("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
98
+ is often the default mode for those terminals that support it: Windows Terminal, WezTerm, ghostty,
99
+ contour, and foot. This library does support any specific "legacy width" measurement, but does
100
+ provide Corrections_ for those terminals without grapheme support.
100
101
 
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.
102
+ See also:
103
+
104
+ - `Grapheme Clusters and Terminal Emulators`_
105
+ - `terminal-unicode-core.tex`_
106
+ - `State of Terminal Emulators in 2025`_
107
+ - `Perfecting Terminal Character Width Using Correction Tables (2026)`_
108
+
109
+ The `jquast/ucs-detect`_ project publishes automatic results of compliance to our standard for Wide
110
+ character, Languages, grapheme clustering, complex or combining scripts, emojis, zero-width joiner,
111
+ variations, and regional indicator (flags) as a `General Tabulated Summary`_ by terminal emulator
112
+ software and version. The results of the ucs-detect project create our correction tables.
105
113
 
106
114
  ========
107
115
  Overview
@@ -148,16 +156,39 @@ Use function `wcswidth()`_ to determine the length of many, a *string of unicode
148
156
  See specification_ of character measurements. Note that ``-1`` is returned if control codes occurs
149
157
  anywhere in the string.
150
158
 
159
+ wcstwidth()
160
+ -----------
161
+
162
+ Same behavior as `wcswidth()`_ with automatic terminal-specific Corrections_, reading
163
+ ``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
164
+ XTVERSION_ or ENQ_ response:
165
+
166
+ .. code-block:: python
167
+
168
+ >>> # '♀️' emoji w/vs-16, uncorrected:
169
+ >>> wcwidth.wcswidth('\u2640\ufe0f')
170
+ 2
171
+ >>> # corrected,
172
+ >>> wcwidth.wcstwidth('\u2640\ufe0f', term_program='vte')
173
+ 1
174
+
151
175
  width()
152
176
  -------
153
177
 
154
- Use function `width()`_ to measure a string with improved handling of ``control_codes``.
178
+ Use function `width()`_ to measure a string with improved handling of ``control_codes`` and
179
+ measurement Corrections_ through ``term_program``:.
155
180
 
156
181
  .. code-block:: python
157
182
 
158
183
  >>> # same support as wcswidth(), eg. regional indicator flag:
159
184
  >>> wcwidth.width('\U0001F1FF\U0001F1FC')
160
185
  2
186
+ >>> # set term_program=True to use wcstwidth()
187
+ >>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program=True)
188
+ 1
189
+ >>> # or set term_program for measurement of a specific terminal
190
+ >>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program='contour')
191
+ 2
161
192
  >>> # but also supports sequences, like SGR colored text, "WARN", followed by reset
162
193
  >>> wcwidth.width('\x1b[38;2;255;150;100mWARN\x1b[0m')
163
194
  4
@@ -190,8 +221,9 @@ terminal sequences for slightly improved performance. Note that TAB (``'\t'``) i
190
221
  character and is also ignored, you may want to use `str.expandtabs()`_, first.
191
222
 
192
223
  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``:
224
+ SGR color, bold, hyperlinks and cursor movement. Any sequence that cannot be accurately parsed
225
+ for horizontal measurement, such as clearing the screen, vertical, or absolute cursor movement will
226
+ raise ``ValueError``:
195
227
 
196
228
  .. code-block:: python
197
229
 
@@ -368,7 +400,7 @@ Use `strip_sequences()`_ to remove all terminal escape sequences from text.
368
400
 
369
401
  .. _ambiguous_width:
370
402
 
371
- ambiguous_width
403
+ Ambiguous Width
372
404
  ---------------
373
405
 
374
406
  Some Unicode characters have "East Asian Ambiguous" (A) width. These characters display as 1 cell by
@@ -376,6 +408,9 @@ default, matching Western terminal contexts, but many CJK (Chinese, Japanese, Ko
376
408
  may have a preference for 2 cells. This is often found as boolean option, "Ambiguous width as wide"
377
409
  in Terminal Emulator software preferences.
378
410
 
411
+ The ``ambiguous_width`` parameter is available on all width-measuring functions: `wcwidth()`_,
412
+ `wcswidth()`_, `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
413
+
379
414
  By default, wcwidth treats ambiguous characters as narrow (width 1). For CJK environments where your
380
415
  terminal is configured to display ambiguous characters as double-width, pass ``ambiguous_width=2``:
381
416
 
@@ -387,9 +422,6 @@ terminal is configured to display ambiguous characters as double-width, pass ``a
387
422
  >>> wcwidth.width('\u2460', ambiguous_width=2)
388
423
  2
389
424
 
390
- The ``ambiguous_width`` parameter is available on all width-measuring functions: `wcwidth()`_,
391
- `wcswidth()`_, `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
392
-
393
425
  **Terminal Detection**
394
426
 
395
427
  The most reliable method to detect whether a terminal profile is set for "Ambiguous width as wide"
@@ -413,6 +445,91 @@ possible timeout, slow network, or non-response when working with "dumb terminal
413
445
  >>> awidth('\u2460')
414
446
  1
415
447
 
448
+ Corrections
449
+ -----------
450
+
451
+ Corrections may be automatically applied depending on the detected or given terminal software name
452
+ beginning with wcwidth release 0.8.0. This allows to correct widths for terminal software that
453
+ differs from the python wcwidth specification_. These corrections are sourced from the
454
+ `jquast/ucs-detect`_ project.
455
+
456
+ The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
457
+ `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
458
+
459
+ `wcstwidth()`_ defaults to ``term_program=True``, auto-detecting the terminal from the
460
+ ``TERM_PROGRAM`` or ``TERM`` environment variable. All other functions default to
461
+ ``term_program=False``, disabling corrections. Use ``term_program=True`` for automatic
462
+ detection by environment values of ``TERM`` and ``TERM_PROGRAM``.
463
+
464
+ .. code-block:: python
465
+
466
+ # VTE terminals (Gnome Terminal Et al.) still render trigrams as narrow (1 cell), but their
467
+ # definition was changed to wide in Unicode 16 (September 2024).
468
+ >>> wcwidth.wcswidth('\u2630')
469
+ 2
470
+ >>> wcwidth.wcstwidth('\u2630', term_program='vte')
471
+ 1
472
+
473
+ # account for Alacritty non-support of emoji ZWJ:
474
+ # man + ZWJ + woman + ZWJ + girl + ZWJ + boy
475
+ >>> family = '\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466'
476
+ >>> wcwidth.wcswidth(family)
477
+ 2
478
+ >>> wcwidth.wcstwidth(family, term_program='alacritty')
479
+ 8
480
+
481
+ Only detectable_ terminals are included: those that identify themselves by XTVERSION_, ENQ_, any
482
+ ``TERM_PROGRAM`` or a unique ``TERM`` environment value. For the most accurate correction tables,
483
+ query the terminal's software version via XTVERSION_ (``CSI > q``) using a higher-level interactive
484
+ terminal library like `jquast/blessed`_:
485
+
486
+ .. code-block:: python
487
+
488
+ >>> import blessed, wcwidth
489
+ >>> term = blessed.Terminal()
490
+ >>> sw_ver = term.get_software_version()
491
+ >>> print(sw_ver)
492
+ SoftwareVersion(name='VTE', version='7600')
493
+ >>> wcwidth.width('\u2630', term_program=sw_ver.name)
494
+ 1
495
+
496
+ This is important because ``TERM_PROGRAM`` is not forwarded for remote hosts, like SSH, and many
497
+ terminals may only be identified using XTVERSION_ or ENQ_. Use `list_term_programs()`_ to see all
498
+ recognized names:
499
+
500
+ .. BEGIN_LIST_TERM_PROGRAMS
501
+ .. code-block:: python
502
+
503
+ >>> wcwidth.list_term_programs()
504
+ ('alacritty', 'apple_terminal', 'bobcat', 'contour', 'extraterm', 'foot',
505
+ 'ghostty', 'hyper', 'iterm.app', 'iterm2', 'kitty', 'konsole', 'mintty',
506
+ 'mlterm', 'pterm', 'putty', 'rio', 'rxvt', 'rxvt-unicode-256color', 'st',
507
+ 'st-256color', 'tabby', 'terminology', 'urxvt', 'vscode', 'vte', 'warp',
508
+ 'warpterminal', 'wezterm', 'xterm', 'xterm-ghostty', 'xterm-kitty',
509
+ 'xterm.js')
510
+
511
+ .. END_LIST_TERM_PROGRAMS
512
+
513
+ ``term_program=False`` (the default for `width()`_, `ljust()`_, `rjust()`_, `center()`_,
514
+ `wrap()`_, and `clip()`_) disables terminal corrections. `wcstwidth()`_ defaults to
515
+ ``term_program=True`` for auto-detection.
516
+
517
+ For automatic tests and other purposes that require cross-environment consistency, set static values
518
+ or unset ``TERM`` and ``TERM_PROGRAM`` environment values, such as in ``conftest.py`` with pytest:
519
+
520
+ .. code-block:: python
521
+
522
+ @pytest.fixture(autouse=True)
523
+ def _clear_term_program():
524
+ """unset TERM/TERM_PROGRAM before each test."""
525
+ saved_term = os.environ.pop('TERM', None)
526
+ saved_tprog = os.environ.pop('TERM_PROGRAM', None)
527
+ yield
528
+ if saved_term is not None:
529
+ os.environ['TERM'] = saved_term
530
+ if saved_tprog is not None:
531
+ os.environ['TERM_PROGRAM'] = saved_tprog
532
+
416
533
  ==========
417
534
  Developing
418
535
  ==========
@@ -536,10 +653,15 @@ This library is used in:
536
653
  Other Languages
537
654
  ===============
538
655
 
539
- There are similar implementations of the `wcwidth()`_ and `wcswidth()`_ functions in other
540
- languages.
656
+ The following libraries provide grapheme and emoji support and closely align with our
657
+ specification_:
658
+
659
+ - `jacobsandlund/uucode`_ Zig
660
+ - `contour-terminal/libunicode`_ C++20
661
+
662
+ There are similar implementations of at least the `wcwidth()`_ and `wcswidth()`_ functions in other
663
+ languages:
541
664
 
542
- - `contour-terminal/libunicode`_: C++20
543
665
  - `ridiculousfish/widecharwidth`_: Python
544
666
  - `termux/wcwidth`_: C
545
667
  - `powerman/wcwidth-icons`_: C
@@ -563,6 +685,24 @@ languages.
563
685
  History
564
686
  =======
565
687
 
688
+ 0.8.1 *2026-06-08*
689
+ * **Improved** `wcstwidth()`_ with new ``zeroer``, ``narrow_wider``, and ``narrow_zeroer``
690
+ Corrections_. `PR #226`_
691
+
692
+ 0.8.0 *2026-06-05*
693
+ * **New** support for Variation Selector 15 Emojis as narrow, `Issue #211`_.
694
+ * **New** argument, ``term_program`` for `wcstwidth()`_, `width()`_, `clip()`_, `wrap()`_,
695
+ `ljust()`_, `rjust()`_, and `center()`_. ``False`` disables Corrections_; ``True``
696
+ auto-detects by ``TERM_PROGRAM`` or ``TERM``; string values accept canonical names matching
697
+ `list_term_programs()`_. `wcstwidth()`_ defaults to ``True``; all other functions
698
+ default to ``False``.
699
+ * **Improved** performance on Python 3.15 using standard library iter_graphemes() `PR #206`_.
700
+ * **Improved** memory usage and import time for Python 3.15 using lazy imports `PR #221`_.
701
+ * **Bugfix** Invisible_Stacker viramas now form conjuncts (Burmese, Khmer, etc.) and
702
+ change some Virama width calculations to match `jacobsandlund/uucode`_ (ghostty) `PR #223`_.
703
+ * **Updated** graphemes width maximum now 2, matching Ghostty, foot, and Windows Terminal `PR
704
+ #224`_.
705
+
566
706
  0.7.0 *2026-05-02*
567
707
  * **New** support for `kitty text sizing protocol`_ (OSC 66) in `width()`_ and `clip()`_.
568
708
  * **New** `clip()`_ parameter ``control_codes='parse'``, ``'ignore'``, and ``'strict'``. `clip()`_
@@ -793,9 +933,15 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
793
933
  .. _`PR #200`: https://github.com/jquast/wcwidth/pull/200
794
934
  .. _`PR #202`: https://github.com/jquast/wcwidth/pull/202
795
935
  .. _`PR #204`: https://github.com/jquast/wcwidth/pull/204
936
+ .. _`PR #206`: https://github.com/jquast/wcwidth/pull/206
937
+ .. _`PR #221`: https://github.com/jquast/wcwidth/pull/221
938
+ .. _`PR #223`: https://github.com/jquast/wcwidth/pull/223
939
+ .. _`PR #224`: https://github.com/jquast/wcwidth/pull/224
940
+ .. _`PR #226`: https://github.com/jquast/wcwidth/pull/226
796
941
  .. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
797
942
  .. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
798
943
  .. _`Issue #190`: https://github.com/jquast/wcwidth/issues/190
944
+ .. _`Issue #211`: https://github.com/jquast/wcwidth/issues/211
799
945
  .. _`jquast/blessed`: https://github.com/jquast/blessed
800
946
  .. _`jquast/telix`: https://github.com/jquast/telix
801
947
  .. _`selectel/pyte`: https://github.com/selectel/pyte
@@ -823,6 +969,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
823
969
  .. _`joshuarubin/wcwidth9`: https://github.com/joshuarubin/wcwidth9
824
970
  .. _`spectreconsole/wcwidth`: https://github.com/spectreconsole/wcwidth
825
971
  .. _`contour-terminal/libunicode`: https://github.com/contour-terminal/libunicode
972
+ .. _`jacobsandlund/uucode`: https://github.com/jacobsandlund/uucode
826
973
  .. _`ridiculousfish/widecharwidth`: https://github.com/ridiculousfish/widecharwidth
827
974
  .. _`termux/wcwidth`: https://github.com/termux/wcwidth
828
975
  .. _`powerman/wcwidth-icons`: https://github.com/powerman/wcwidth-icons
@@ -834,7 +981,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
834
981
  .. _`ihabunek/toot`: https://github.com/ihabunek/toot
835
982
  .. _`saulpw/visidata`: https://github.com/saulpw/visidata
836
983
  .. _`urwid/urwid`: https://github.com/urwid/urwid
837
- .. _`prettytable/prettytable`: https://github.com/urwid/urwid
984
+ .. _`prettytable/prettytable`: https://github.com/prettytable/prettytable
838
985
  .. _`leviathan0992/Pylsy`: https://github.com/leviathan0992/Pylsy
839
986
  .. _`pip-tools`: https://pip-tools.readthedocs.io/
840
987
  .. _`sphinx`: https://www.sphinx-doc.org/
@@ -846,6 +993,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
846
993
  .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
847
994
  .. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
848
995
  .. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
996
+ .. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
849
997
  .. _`width()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.width
850
998
  .. _`iter_graphemes()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes
851
999
  .. _`iter_graphemes_reverse()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes_reverse
@@ -861,6 +1009,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
861
1009
  .. _`TextSizingParams`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.TextSizingParams
862
1010
  .. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
863
1011
  .. _`list_versions()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_versions
1012
+ .. _`list_term_programs()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_term_programs
864
1013
  .. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
865
1014
  .. _`Terminal.detect_ambiguous_width()`: https://blessed.readthedocs.io/en/latest/api/terminal.html#blessed.terminal.Terminal.detect_ambiguous_width
866
1015
  .. _`parity padding`: https://jazcap53.github.io/pythons-eccentric-strcenter.html
@@ -868,6 +1017,10 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
868
1017
  .. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
869
1018
  .. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
870
1019
  .. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
1020
+ .. _`Perfecting Terminal Character Width Using Correction Tables (2026)`: https://www.jeffquast.com/post/perfecting-terminal-character-width-using-correction-tables/
1021
+ .. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
1022
+ .. _ENQ: https://documentation.help/PuTTY/config-answerback.html
1023
+ .. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
871
1024
  .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi
872
1025
  :alt: Downloads
873
1026
  :target: https://pypi.org/project/wcwidth/