wcwidth 0.7.0__tar.gz → 0.8.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.
Files changed (149) hide show
  1. {wcwidth-0.7.0 → wcwidth-0.8.0}/.pylintrc +2 -0
  2. {wcwidth-0.7.0 → wcwidth-0.8.0}/PKG-INFO +173 -26
  3. {wcwidth-0.7.0 → wcwidth-0.8.0}/bin/update-tables.py +592 -51
  4. wcwidth-0.8.0/code_templates/grapheme_override_per_terminal.py.j2 +13 -0
  5. wcwidth-0.8.0/code_templates/grapheme_override_table.py.j2 +18 -0
  6. wcwidth-0.8.0/code_templates/grapheme_registry.py.j2 +13 -0
  7. wcwidth-0.8.0/code_templates/table_overrides.py.j2 +37 -0
  8. wcwidth-0.8.0/code_templates/term_programs.py.j2 +22 -0
  9. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/api.rst +6 -0
  10. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/intro.rst +171 -25
  11. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/requirements.txt +2 -2
  12. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/specs.rst +38 -22
  13. {wcwidth-0.7.0 → wcwidth-0.8.0}/pyproject.toml +5 -1
  14. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-tests39.txt +3 -4
  15. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-update.in +1 -0
  16. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-update.txt +7 -4
  17. wcwidth-0.8.0/tests/conftest.py +38 -0
  18. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_benchmarks.py +84 -2
  19. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_clip.py +2 -2
  20. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_core.py +48 -11
  21. wcwidth-0.8.0/tests/test_term_overrides.py +493 -0
  22. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_text_sizing.py +2 -1
  23. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_width.py +6 -0
  24. {wcwidth-0.7.0 → wcwidth-0.8.0}/tox.ini +7 -3
  25. wcwidth-0.8.0/ucs-detect/data/AbsoluteTelnetSSH.yaml +789 -0
  26. wcwidth-0.8.0/ucs-detect/data/_linuxfbdev.yaml +50 -0
  27. wcwidth-0.8.0/ucs-detect/data/_syncterm.yaml +38 -0
  28. wcwidth-0.8.0/ucs-detect/data/alacritty.yaml +8755 -0
  29. wcwidth-0.8.0/ucs-detect/data/bobcat.yaml +10780 -0
  30. wcwidth-0.8.0/ucs-detect/data/conemu.exe.yaml +29154 -0
  31. wcwidth-0.8.0/ucs-detect/data/contour.yaml +16580 -0
  32. wcwidth-0.8.0/ucs-detect/data/coolretroterm.yaml +10905 -0
  33. wcwidth-0.8.0/ucs-detect/data/extraterm.yaml +27161 -0
  34. wcwidth-0.8.0/ucs-detect/data/foot.yaml +8336 -0
  35. wcwidth-0.8.0/ucs-detect/data/ghostty.yaml +1001 -0
  36. wcwidth-0.8.0/ucs-detect/data/gnometerminal.yaml +13095 -0
  37. wcwidth-0.8.0/ucs-detect/data/hyper.yaml +12481 -0
  38. wcwidth-0.8.0/ucs-detect/data/iterm2.yaml +6576 -0
  39. wcwidth-0.8.0/ucs-detect/data/kitty.yaml +15571 -0
  40. wcwidth-0.8.0/ucs-detect/data/konsole.yaml +6187 -0
  41. wcwidth-0.8.0/ucs-detect/data/libvterm.yaml +11631 -0
  42. wcwidth-0.8.0/ucs-detect/data/lxterminal.yaml +12887 -0
  43. wcwidth-0.8.0/ucs-detect/data/mintty.yaml +27871 -0
  44. wcwidth-0.8.0/ucs-detect/data/mlterm.yaml +14706 -0
  45. wcwidth-0.8.0/ucs-detect/data/putty.yaml +10658 -0
  46. wcwidth-0.8.0/ucs-detect/data/qterminal.yaml +8394 -0
  47. wcwidth-0.8.0/ucs-detect/data/rio.yaml +9924 -0
  48. wcwidth-0.8.0/ucs-detect/data/rxvtunicode.yaml +10826 -0
  49. wcwidth-0.8.0/ucs-detect/data/screen.yaml +18026 -0
  50. wcwidth-0.8.0/ucs-detect/data/securecrt.yaml +16450 -0
  51. wcwidth-0.8.0/ucs-detect/data/st.yaml +10759 -0
  52. wcwidth-0.8.0/ucs-detect/data/tabby.yaml +12135 -0
  53. wcwidth-0.8.0/ucs-detect/data/teraterm.yaml +6678 -0
  54. wcwidth-0.8.0/ucs-detect/data/terminalapp.yaml +10895 -0
  55. wcwidth-0.8.0/ucs-detect/data/terminalexe.yaml +5649 -0
  56. wcwidth-0.8.0/ucs-detect/data/terminator.yaml +13065 -0
  57. wcwidth-0.8.0/ucs-detect/data/terminology.yaml +26742 -0
  58. wcwidth-0.8.0/ucs-detect/data/tmux.yaml +6895 -0
  59. wcwidth-0.8.0/ucs-detect/data/vscodeterminal.yaml +11720 -0
  60. wcwidth-0.8.0/ucs-detect/data/warp.yaml +11038 -0
  61. wcwidth-0.8.0/ucs-detect/data/westonterminal.yaml +26416 -0
  62. wcwidth-0.8.0/ucs-detect/data/wezterm.yaml +10565 -0
  63. wcwidth-0.8.0/ucs-detect/data/xfce4terminal.yaml +13020 -0
  64. wcwidth-0.8.0/ucs-detect/data/xterm.yaml +10881 -0
  65. wcwidth-0.8.0/ucs-detect/data/zellij.yaml +12096 -0
  66. wcwidth-0.8.0/ucs-detect/data/zutty.yaml +10797 -0
  67. wcwidth-0.8.0/wcwidth/__init__.py +81 -0
  68. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/_clip.py +31 -5
  69. wcwidth-0.8.0/wcwidth/_constants.py +151 -0
  70. wcwidth-0.8.0/wcwidth/_wcswidth.py +427 -0
  71. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/_wcwidth.py +4 -0
  72. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/_width.py +220 -56
  73. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/align.py +32 -3
  74. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/grapheme.py +67 -2
  75. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/table_grapheme.py +53 -0
  76. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/__init__.py +37 -0
  77. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_27e0693f.py +2677 -0
  78. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_45d92e98.py +1969 -0
  79. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_4cdf59ce.py +42 -0
  80. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_50bf0759.py +2571 -0
  81. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_529fbb4a.py +1799 -0
  82. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_5bfac390.py +2571 -0
  83. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_813fee16.py +2570 -0
  84. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_8589765c.py +1310 -0
  85. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_8f94b404.py +2581 -0
  86. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_970dbe10.py +2839 -0
  87. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c0a2cdbf.py +3617 -0
  88. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c0d5dc9e.py +6427 -0
  89. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c2157f7e.py +1153 -0
  90. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c3db41c0.py +3401 -0
  91. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_da9ceb0a.py +1910 -0
  92. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_e08bd75e.py +2641 -0
  93. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_e22030f3.py +6396 -0
  94. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_fcc05a0f.py +6648 -0
  95. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_fd9d4c44.py +3671 -0
  96. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_registry.py +32 -0
  97. wcwidth-0.8.0/wcwidth/table_overrides.py +714 -0
  98. wcwidth-0.8.0/wcwidth/table_term_programs.py +49 -0
  99. wcwidth-0.8.0/wcwidth/table_vs15.py +104 -0
  100. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/text_sizing.py +3 -1
  101. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/textwrap.py +20 -1
  102. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/unicode_versions.py +1 -1
  103. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/wcwidth.py +28 -9
  104. wcwidth-0.7.0/tests/conftest.py +0 -17
  105. wcwidth-0.7.0/wcwidth/__init__.py +0 -52
  106. wcwidth-0.7.0/wcwidth/_constants.py +0 -65
  107. wcwidth-0.7.0/wcwidth/_wcswidth.py +0 -150
  108. {wcwidth-0.7.0 → wcwidth-0.8.0}/.gitignore +0 -0
  109. {wcwidth-0.7.0 → wcwidth-0.8.0}/LICENSE +0 -0
  110. {wcwidth-0.7.0 → wcwidth-0.8.0}/README.rst +0 -0
  111. {wcwidth-0.7.0 → wcwidth-0.8.0}/bin/show-sequences +0 -0
  112. {wcwidth-0.7.0 → wcwidth-0.8.0}/bin/strip-sequences +0 -0
  113. {wcwidth-0.7.0 → wcwidth-0.8.0}/bin/verify-table-integrity.py +0 -0
  114. {wcwidth-0.7.0 → wcwidth-0.8.0}/bin/wcwidth-browser.py +0 -0
  115. {wcwidth-0.7.0 → wcwidth-0.8.0}/bin/wcwidth-libc-comparator.py +0 -0
  116. {wcwidth-0.7.0 → wcwidth-0.8.0}/code_templates/grapheme_table.py.j2 +0 -0
  117. {wcwidth-0.7.0 → wcwidth-0.8.0}/code_templates/python_table.py.j2 +0 -0
  118. {wcwidth-0.7.0 → wcwidth-0.8.0}/code_templates/unicode_version.rst.j2 +0 -0
  119. {wcwidth-0.7.0 → wcwidth-0.8.0}/code_templates/unicode_versions.py.j2 +0 -0
  120. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/conf.py +0 -0
  121. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/index.rst +0 -0
  122. {wcwidth-0.7.0 → wcwidth-0.8.0}/docs/unicode_version.rst +0 -0
  123. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-develop.txt +0 -0
  124. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-docs.in +0 -0
  125. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-tests38.in +0 -0
  126. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-tests38.txt +0 -0
  127. {wcwidth-0.7.0 → wcwidth-0.8.0}/requirements-tests39.in +0 -0
  128. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/__init__.py +0 -0
  129. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_ambiguous.py +0 -0
  130. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_clip_cjk_emoji.py +0 -0
  131. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_clip_overtyping.py +0 -0
  132. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_emojis.py +0 -0
  133. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_grapheme.py +0 -0
  134. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_hyperlink.py +0 -0
  135. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_justify.py +0 -0
  136. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_sgr_state.py +0 -0
  137. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_textwrap.py +0 -0
  138. {wcwidth-0.7.0 → wcwidth-0.8.0}/tests/test_ucslevel.py +0 -0
  139. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/bisearch.py +0 -0
  140. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/control_codes.py +0 -0
  141. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/escape_sequences.py +0 -0
  142. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/hyperlink.py +0 -0
  143. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/py.typed +0 -0
  144. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/sgr_state.py +0 -0
  145. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/table_ambiguous.py +0 -0
  146. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/table_mc.py +0 -0
  147. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/table_vs16.py +0 -0
  148. {wcwidth-0.7.0 → wcwidth-0.8.0}/wcwidth/table_wide.py +0 -0
  149. {wcwidth-0.7.0 → wcwidth-0.8.0}/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.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>
@@ -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
+ - `Report of terminals supporting Graphemes (2027)`_
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,90 @@ 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 are automatically applied depending on 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 standard. These corrections are sourced from the `jquast/ucs-detect`_ project.
454
+
455
+ The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
456
+ `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
457
+
458
+ `wcstwidth()`_ defaults to ``term_program=True``, auto-detecting the terminal from the
459
+ ``TERM_PROGRAM`` or ``TERM`` environment variable. All other functions default to
460
+ ``term_program=False``, disabling corrections. Use ``term_program=True`` for automatic
461
+ detection by environment values of ``TERM`` and ``TERM_PROGRAM``.
462
+
463
+ .. code-block:: python
464
+
465
+ # VTE terminals (Gnome Terminal Et al.) still render trigrams as narrow (1 cell), but their
466
+ # definition was changed to wide in Unicode 16 (September 2024).
467
+ >>> wcwidth.wcswidth('\u2630')
468
+ 2
469
+ >>> wcwidth.wcstwidth('\u2630', term_program='vte')
470
+ 1
471
+
472
+ # account for Alacritty non-support of emoji ZWJ:
473
+ # man + ZWJ + woman + ZWJ + girl + ZWJ + boy
474
+ >>> family = '\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466'
475
+ >>> wcwidth.wcswidth(family)
476
+ 2
477
+ >>> wcwidth.wcstwidth(family, term_program='alacritty')
478
+ 8
479
+
480
+ Only detectable_ terminals are included: those that identify themselves by XTVERSION_, ENQ_, any
481
+ ``TERM_PROGRAM`` or a unique ``TERM`` environment value. For the most accurate correction tables,
482
+ query the terminal's software version via XTVERSION_ (``CSI > q``) using a higher-level interactive
483
+ terminal library like `jquast/blessed`_:
484
+
485
+ .. code-block:: python
486
+
487
+ >>> import blessed, wcwidth
488
+ >>> term = blessed.Terminal()
489
+ >>> sw_ver = term.get_software_version()
490
+ >>> print(sw_ver)
491
+ SoftwareVersion(name='VTE', version='7600')
492
+ >>> wcwidth.width('\u2630', term_program=sw_ver.name)
493
+ 1
494
+
495
+ This is important because ``TERM_PROGRAM`` is not forwarded for remote hosts, like SSH, and many
496
+ terminals may only be identified using XTVERSION_ or ENQ_. Use `list_term_programs()`_ to see all
497
+ recognized names:
498
+
499
+ .. BEGIN_LIST_TERM_PROGRAMS
500
+ .. code-block:: python
501
+
502
+ >>> wcwidth.list_term_programs()
503
+ ('alacritty', 'apple_terminal', 'bobcat', 'contour', 'extraterm', 'foot',
504
+ 'ghostty', 'hyper', 'iterm.app', 'iterm2', 'kitty', 'konsole', 'mintty',
505
+ 'mlterm', 'pterm', 'putty', 'rio', 'rxvt', 'rxvt-unicode-256color', 'st',
506
+ 'st-256color', 'tabby', 'terminology', 'urxvt', 'vscode', 'vte', 'warp',
507
+ 'warpterminal', 'wezterm', 'xterm', 'xterm-ghostty', 'xterm-kitty',
508
+ 'xterm.js')
509
+
510
+ .. END_LIST_TERM_PROGRAMS
511
+
512
+ ``term_program=False`` (the default for `width()`_, `ljust()`_, `rjust()`_, `center()`_,
513
+ `wrap()`_, and `clip()`_) disables terminal corrections. `wcstwidth()`_ defaults to
514
+ ``term_program=True`` for auto-detection.
515
+
516
+ For automatic tests and other purposes that require cross-environment consistency, set static values
517
+ or unset ``TERM`` and ``TERM_PROGRAM`` environment values, such as in ``conftest.py`` with pytest:
518
+
519
+ .. code-block:: python
520
+
521
+ @pytest.fixture(autouse=True)
522
+ def _clear_term_program():
523
+ """unset TERM/TERM_PROGRAM before each test."""
524
+ saved_term = os.environ.pop('TERM', None)
525
+ saved_tprog = os.environ.pop('TERM_PROGRAM', None)
526
+ yield
527
+ if saved_term is not None:
528
+ os.environ['TERM'] = saved_term
529
+ if saved_tprog is not None:
530
+ os.environ['TERM_PROGRAM'] = saved_tprog
531
+
416
532
  ==========
417
533
  Developing
418
534
  ==========
@@ -536,10 +652,15 @@ This library is used in:
536
652
  Other Languages
537
653
  ===============
538
654
 
539
- There are similar implementations of the `wcwidth()`_ and `wcswidth()`_ functions in other
540
- languages.
655
+ The following libraries provide grapheme and emoji support and closely align with our
656
+ specification_:
657
+
658
+ - `jacobsandlund/uucode`_ Zig
659
+ - `contour-terminal/libunicode`_ C++20
660
+
661
+ There are similar implementations of at least the `wcwidth()`_ and `wcswidth()`_ functions in other
662
+ languages:
541
663
 
542
- - `contour-terminal/libunicode`_: C++20
543
664
  - `ridiculousfish/widecharwidth`_: Python
544
665
  - `termux/wcwidth`_: C
545
666
  - `powerman/wcwidth-icons`_: C
@@ -563,6 +684,20 @@ languages.
563
684
  History
564
685
  =======
565
686
 
687
+ 0.8.0 *(unreleased)*
688
+ * **New** support for Variation Selector 15 Emojis as narrow, `Issue #211`_.
689
+ * **New** argument, ``term_program`` for `wcstwidth()`_, `width()`_, `clip()`_, `wrap()`_,
690
+ `ljust()`_, `rjust()`_, and `center()`_. ``False`` disables corrections; ``True``
691
+ auto-detects by ``TERM_PROGRAM`` or ``TERM``; string values accept canonical names matching
692
+ `list_term_programs()`_. `wcstwidth()`_ defaults to ``True``; all other functions
693
+ default to ``False``.
694
+ * **Improved** performance on Python 3.15 using standard library iter_graphemes() `PR #206`_.
695
+ * **Improved** memory usage and import time for Python 3.15 using lazy imports `PR #221`_.
696
+ * **Bugfix** Invisible_Stacker viramas now form conjuncts (Burmese, Khmer, etc.) and
697
+ change some Virama width calculations to match `jacobsandlund/uucode`_ (ghostty) `PR #223`_.
698
+ * **Updated** graphemes width maximum now 2, matching Ghostty, foot, and Windows Terminal `PR
699
+ #224`_.
700
+
566
701
  0.7.0 *2026-05-02*
567
702
  * **New** support for `kitty text sizing protocol`_ (OSC 66) in `width()`_ and `clip()`_.
568
703
  * **New** `clip()`_ parameter ``control_codes='parse'``, ``'ignore'``, and ``'strict'``. `clip()`_
@@ -793,9 +928,14 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
793
928
  .. _`PR #200`: https://github.com/jquast/wcwidth/pull/200
794
929
  .. _`PR #202`: https://github.com/jquast/wcwidth/pull/202
795
930
  .. _`PR #204`: https://github.com/jquast/wcwidth/pull/204
931
+ .. _`PR #206`: https://github.com/jquast/wcwidth/pull/206
932
+ .. _`PR #221`: https://github.com/jquast/wcwidth/pull/221
933
+ .. _`PR #223`: https://github.com/jquast/wcwidth/pull/223
934
+ .. _`PR #224`: https://github.com/jquast/wcwidth/pull/224
796
935
  .. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
797
936
  .. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
798
937
  .. _`Issue #190`: https://github.com/jquast/wcwidth/issues/190
938
+ .. _`Issue #211`: https://github.com/jquast/wcwidth/issues/211
799
939
  .. _`jquast/blessed`: https://github.com/jquast/blessed
800
940
  .. _`jquast/telix`: https://github.com/jquast/telix
801
941
  .. _`selectel/pyte`: https://github.com/selectel/pyte
@@ -823,6 +963,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
823
963
  .. _`joshuarubin/wcwidth9`: https://github.com/joshuarubin/wcwidth9
824
964
  .. _`spectreconsole/wcwidth`: https://github.com/spectreconsole/wcwidth
825
965
  .. _`contour-terminal/libunicode`: https://github.com/contour-terminal/libunicode
966
+ .. _`jacobsandlund/uucode`: https://github.com/jacobsandlund/uucode
826
967
  .. _`ridiculousfish/widecharwidth`: https://github.com/ridiculousfish/widecharwidth
827
968
  .. _`termux/wcwidth`: https://github.com/termux/wcwidth
828
969
  .. _`powerman/wcwidth-icons`: https://github.com/powerman/wcwidth-icons
@@ -834,7 +975,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
834
975
  .. _`ihabunek/toot`: https://github.com/ihabunek/toot
835
976
  .. _`saulpw/visidata`: https://github.com/saulpw/visidata
836
977
  .. _`urwid/urwid`: https://github.com/urwid/urwid
837
- .. _`prettytable/prettytable`: https://github.com/urwid/urwid
978
+ .. _`prettytable/prettytable`: https://github.com/prettytable/prettytable
838
979
  .. _`leviathan0992/Pylsy`: https://github.com/leviathan0992/Pylsy
839
980
  .. _`pip-tools`: https://pip-tools.readthedocs.io/
840
981
  .. _`sphinx`: https://www.sphinx-doc.org/
@@ -846,6 +987,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
846
987
  .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
847
988
  .. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
848
989
  .. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
990
+ .. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
849
991
  .. _`width()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.width
850
992
  .. _`iter_graphemes()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes
851
993
  .. _`iter_graphemes_reverse()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes_reverse
@@ -861,6 +1003,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
861
1003
  .. _`TextSizingParams`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.TextSizingParams
862
1004
  .. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
863
1005
  .. _`list_versions()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_versions
1006
+ .. _`list_term_programs()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_term_programs
864
1007
  .. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
865
1008
  .. _`Terminal.detect_ambiguous_width()`: https://blessed.readthedocs.io/en/latest/api/terminal.html#blessed.terminal.Terminal.detect_ambiguous_width
866
1009
  .. _`parity padding`: https://jazcap53.github.io/pythons-eccentric-strcenter.html
@@ -868,6 +1011,10 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
868
1011
  .. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
869
1012
  .. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
870
1013
  .. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
1014
+ .. _`Report of terminals supporting Graphemes (2027)`: https://ucs-detect.readthedocs.io/results.html#terminal-features
1015
+ .. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
1016
+ .. _ENQ: https://documentation.help/PuTTY/config-answerback.html
1017
+ .. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
871
1018
  .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi
872
1019
  :alt: Downloads
873
1020
  :target: https://pypi.org/project/wcwidth/