wcwidth 0.8.0__tar.gz → 0.8.2__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 (151) hide show
  1. {wcwidth-0.8.0 → wcwidth-0.8.2}/PKG-INFO +17 -7
  2. {wcwidth-0.8.0 → wcwidth-0.8.2}/bin/update-tables.py +23 -5
  3. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/grapheme_override_per_terminal.py.j2 +1 -1
  4. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/grapheme_registry.py.j2 +1 -1
  5. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/table_overrides.py.j2 +7 -4
  6. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/intro.rst +16 -6
  7. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/specs.rst +2 -2
  8. {wcwidth-0.8.0 → wcwidth-0.8.2}/pyproject.toml +1 -1
  9. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_core.py +6 -0
  10. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_term_overrides.py +105 -1
  11. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/alacritty.yaml +97 -31
  12. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/bobcat.yaml +31 -7
  13. wcwidth-0.8.0/ucs-detect/data/extraterm.yaml → wcwidth-0.8.2/ucs-detect/data/conemu.exe.yaml +1072 -625
  14. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/contour.yaml +10809 -5811
  15. wcwidth-0.8.2/ucs-detect/data/coolretroterm.yaml +10734 -0
  16. wcwidth-0.8.2/ucs-detect/data/emoji-test-latest.txt +5518 -0
  17. wcwidth-0.8.2/ucs-detect/data/emoji-variation-sequences-latest.txt +757 -0
  18. wcwidth-0.8.2/ucs-detect/data/emoji-zwj-sequences-latest.txt +1675 -0
  19. wcwidth-0.8.0/ucs-detect/data/conemu.exe.yaml → wcwidth-0.8.2/ucs-detect/data/extraterm.yaml +20909 -16815
  20. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/foot.yaml +61 -139
  21. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/ghostty.yaml +32 -23
  22. wcwidth-0.8.0/ucs-detect/data/xfce4terminal.yaml → wcwidth-0.8.2/ucs-detect/data/gnometerminal.yaml +4807 -1580
  23. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/hyper.yaml +899 -827
  24. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/iterm2.yaml +15 -27
  25. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/kitty.yaml +293 -221
  26. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/konsole.yaml +1845 -408
  27. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/libvterm.yaml +7624 -5719
  28. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/lxterminal.yaml +1206 -1134
  29. wcwidth-0.8.0/ucs-detect/data/terminology.yaml → wcwidth-0.8.2/ucs-detect/data/mintty.yaml +5186 -5682
  30. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/mlterm.yaml +8223 -5451
  31. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/putty.yaml +65 -41
  32. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/qterminal.yaml +3290 -2531
  33. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/rio.yaml +5820 -4398
  34. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/rxvtunicode.yaml +46 -136
  35. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/screen.yaml +10626 -9206
  36. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/st.yaml +45 -135
  37. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/tabby.yaml +6382 -4876
  38. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/teraterm.yaml +11 -5
  39. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/terminalapp.yaml +14 -8
  40. wcwidth-0.8.2/ucs-detect/data/terminalexe.yaml +3078 -0
  41. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/terminator.yaml +4024 -1781
  42. wcwidth-0.8.2/ucs-detect/data/terminology.yaml +31854 -0
  43. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/tmux.yaml +2003 -527
  44. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/warp.yaml +6827 -4568
  45. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/wezterm.yaml +309 -303
  46. wcwidth-0.8.0/ucs-detect/data/gnometerminal.yaml → wcwidth-0.8.2/ucs-detect/data/xfce4terminal.yaml +1558 -1613
  47. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/xterm.yaml +44 -131
  48. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/zellij.yaml +6607 -5224
  49. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/zutty.yaml +74 -167
  50. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/__init__.py +1 -1
  51. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/_constants.py +17 -4
  52. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/_wcswidth.py +14 -2
  53. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/_width.py +12 -0
  54. wcwidth-0.8.0/wcwidth/table_grapheme_overrides/_known_c0d5dc9e.py → wcwidth-0.8.2/wcwidth/table_grapheme_overrides/_known_3d4826b8.py +6068 -6033
  55. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_registry.py +1 -1
  56. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_overrides.py +408 -269
  57. wcwidth-0.8.0/ucs-detect/data/coolretroterm.yaml +0 -10905
  58. wcwidth-0.8.0/ucs-detect/data/mintty.yaml +0 -27871
  59. wcwidth-0.8.0/ucs-detect/data/terminalexe.yaml +0 -5649
  60. {wcwidth-0.8.0 → wcwidth-0.8.2}/.gitignore +0 -0
  61. {wcwidth-0.8.0 → wcwidth-0.8.2}/.pylintrc +0 -0
  62. {wcwidth-0.8.0 → wcwidth-0.8.2}/LICENSE +0 -0
  63. {wcwidth-0.8.0 → wcwidth-0.8.2}/README.rst +0 -0
  64. {wcwidth-0.8.0 → wcwidth-0.8.2}/bin/show-sequences +0 -0
  65. {wcwidth-0.8.0 → wcwidth-0.8.2}/bin/strip-sequences +0 -0
  66. {wcwidth-0.8.0 → wcwidth-0.8.2}/bin/verify-table-integrity.py +0 -0
  67. {wcwidth-0.8.0 → wcwidth-0.8.2}/bin/wcwidth-browser.py +0 -0
  68. {wcwidth-0.8.0 → wcwidth-0.8.2}/bin/wcwidth-libc-comparator.py +0 -0
  69. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/grapheme_override_table.py.j2 +0 -0
  70. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/grapheme_table.py.j2 +0 -0
  71. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/python_table.py.j2 +0 -0
  72. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/term_programs.py.j2 +0 -0
  73. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/unicode_version.rst.j2 +0 -0
  74. {wcwidth-0.8.0 → wcwidth-0.8.2}/code_templates/unicode_versions.py.j2 +0 -0
  75. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/api.rst +0 -0
  76. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/conf.py +0 -0
  77. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/index.rst +0 -0
  78. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/requirements.txt +0 -0
  79. {wcwidth-0.8.0 → wcwidth-0.8.2}/docs/unicode_version.rst +0 -0
  80. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-develop.txt +0 -0
  81. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-docs.in +0 -0
  82. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-tests38.in +0 -0
  83. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-tests38.txt +0 -0
  84. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-tests39.in +0 -0
  85. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-tests39.txt +0 -0
  86. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-update.in +0 -0
  87. {wcwidth-0.8.0 → wcwidth-0.8.2}/requirements-update.txt +0 -0
  88. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/__init__.py +0 -0
  89. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/conftest.py +0 -0
  90. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_ambiguous.py +0 -0
  91. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_benchmarks.py +0 -0
  92. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_clip.py +0 -0
  93. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_clip_cjk_emoji.py +0 -0
  94. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_clip_overtyping.py +0 -0
  95. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_emojis.py +0 -0
  96. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_grapheme.py +0 -0
  97. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_hyperlink.py +0 -0
  98. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_justify.py +0 -0
  99. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_sgr_state.py +0 -0
  100. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_text_sizing.py +0 -0
  101. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_textwrap.py +0 -0
  102. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_ucslevel.py +0 -0
  103. {wcwidth-0.8.0 → wcwidth-0.8.2}/tests/test_width.py +0 -0
  104. {wcwidth-0.8.0 → wcwidth-0.8.2}/tox.ini +0 -0
  105. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/AbsoluteTelnetSSH.yaml +0 -0
  106. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/_linuxfbdev.yaml +0 -0
  107. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/_syncterm.yaml +0 -0
  108. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/securecrt.yaml +0 -0
  109. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/vscodeterminal.yaml +0 -0
  110. {wcwidth-0.8.0 → wcwidth-0.8.2}/ucs-detect/data/westonterminal.yaml +0 -0
  111. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/_clip.py +0 -0
  112. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/_wcwidth.py +0 -0
  113. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/align.py +0 -0
  114. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/bisearch.py +0 -0
  115. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/control_codes.py +0 -0
  116. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/escape_sequences.py +0 -0
  117. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/grapheme.py +0 -0
  118. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/hyperlink.py +0 -0
  119. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/py.typed +0 -0
  120. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/sgr_state.py +0 -0
  121. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_ambiguous.py +0 -0
  122. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme.py +0 -0
  123. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/__init__.py +0 -0
  124. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_27e0693f.py +2478 -2478
  125. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_45d92e98.py +1677 -1677
  126. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_4cdf59ce.py +0 -0
  127. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_50bf0759.py +2437 -2437
  128. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_529fbb4a.py +1470 -1470
  129. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_5bfac390.py +2437 -2437
  130. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_813fee16.py +2437 -2437
  131. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_8589765c.py +870 -870
  132. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_8f94b404.py +2437 -2437
  133. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_970dbe10.py +2693 -2693
  134. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_c0a2cdbf.py +3246 -3246
  135. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_c2157f7e.py +906 -906
  136. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_c3db41c0.py +3199 -3199
  137. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_da9ceb0a.py +1598 -1598
  138. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_e08bd75e.py +2461 -2461
  139. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_e22030f3.py +5927 -5927
  140. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_fcc05a0f.py +6173 -6173
  141. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_grapheme_overrides/_known_fd9d4c44.py +3300 -3300
  142. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_mc.py +0 -0
  143. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_term_programs.py +0 -0
  144. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_vs15.py +0 -0
  145. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_vs16.py +0 -0
  146. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_wide.py +0 -0
  147. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/table_zero.py +0 -0
  148. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/text_sizing.py +0 -0
  149. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/textwrap.py +0 -0
  150. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/unicode_versions.py +0 -0
  151. {wcwidth-0.8.0 → wcwidth-0.8.2}/wcwidth/wcwidth.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wcwidth
3
- Version: 0.8.0
3
+ Version: 0.8.2
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>
@@ -104,7 +104,7 @@ See also:
104
104
  - `Grapheme Clusters and Terminal Emulators`_
105
105
  - `terminal-unicode-core.tex`_
106
106
  - `State of Terminal Emulators in 2025`_
107
- - `Report of terminals supporting Graphemes (2027)`_
107
+ - `Perfecting Terminal Character Width Using Correction Tables (2026)`_
108
108
 
109
109
  The `jquast/ucs-detect`_ project publishes automatic results of compliance to our standard for Wide
110
110
  character, Languages, grapheme clustering, complex or combining scripts, emojis, zero-width joiner,
@@ -448,9 +448,10 @@ possible timeout, slow network, or non-response when working with "dumb terminal
448
448
  Corrections
449
449
  -----------
450
450
 
451
- Corrections are automatically applied depending on detected or given terminal software name
451
+ Corrections may be automatically applied depending on the detected or given terminal software name
452
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.
453
+ differs from the python wcwidth specification_. These corrections are sourced from the
454
+ `jquast/ucs-detect`_ project.
454
455
 
455
456
  The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
456
457
  `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
@@ -684,10 +685,18 @@ languages:
684
685
  History
685
686
  =======
686
687
 
687
- 0.8.0 *(unreleased)*
688
+ 0.8.2 *2026-06-29*
689
+ * **Bugfix** Do not raise IndexError when given legacy POSIX ``n`` argument to `wcswidth()`_ or
690
+ `wcstwidth()`_ exceed string length without raising IndexError
691
+
692
+ 0.8.1 *2026-06-08*
693
+ * **Improved** `wcstwidth()`_ with new ``zeroer``, ``narrow_wider``, and ``narrow_zeroer``
694
+ Corrections_. `PR #226`_
695
+
696
+ 0.8.0 *2026-06-05*
688
697
  * **New** support for Variation Selector 15 Emojis as narrow, `Issue #211`_.
689
698
  * **New** argument, ``term_program`` for `wcstwidth()`_, `width()`_, `clip()`_, `wrap()`_,
690
- `ljust()`_, `rjust()`_, and `center()`_. ``False`` disables corrections; ``True``
699
+ `ljust()`_, `rjust()`_, and `center()`_. ``False`` disables Corrections_; ``True``
691
700
  auto-detects by ``TERM_PROGRAM`` or ``TERM``; string values accept canonical names matching
692
701
  `list_term_programs()`_. `wcstwidth()`_ defaults to ``True``; all other functions
693
702
  default to ``False``.
@@ -932,6 +941,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
932
941
  .. _`PR #221`: https://github.com/jquast/wcwidth/pull/221
933
942
  .. _`PR #223`: https://github.com/jquast/wcwidth/pull/223
934
943
  .. _`PR #224`: https://github.com/jquast/wcwidth/pull/224
944
+ .. _`PR #226`: https://github.com/jquast/wcwidth/pull/226
935
945
  .. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
936
946
  .. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
937
947
  .. _`Issue #190`: https://github.com/jquast/wcwidth/issues/190
@@ -1011,7 +1021,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
1011
1021
  .. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
1012
1022
  .. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
1013
1023
  .. _`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
1024
+ .. _`Perfecting Terminal Character Width Using Correction Tables (2026)`: https://www.jeffquast.com/post/perfecting-terminal-character-width-using-correction-tables/
1015
1025
  .. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
1016
1026
  .. _ENQ: https://documentation.help/PuTTY/config-answerback.html
1017
1027
  .. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
@@ -1246,12 +1246,17 @@ def parse_wchar_codepoint(wchar: str) -> int:
1246
1246
 
1247
1247
  @dataclass(frozen=True)
1248
1248
  class TerminalOverrides:
1249
- """Per-terminal codepoint override ranges for narrower/wider measurements."""
1249
+ """Per-terminal codepoint override ranges for narrower/wider/zeroer measurements."""
1250
1250
  narrower: list[tuple[str, str, str]]
1251
1251
  wider: list[tuple[str, str, str]]
1252
+ zeroer: list[tuple[str, str, str]]
1253
+ narrow_zeroer: list[tuple[str, str, str]]
1252
1254
 
1253
1255
  def items(self) -> list[tuple[str, list[tuple[str, str, str]]]]:
1254
- return [('narrower', self.narrower), ('wider', self.wider)]
1256
+ return [('narrower', self.narrower),
1257
+ ('wider', self.wider),
1258
+ ('zeroer', self.zeroer),
1259
+ ('narrow_zeroer', self.narrow_zeroer)]
1255
1260
 
1256
1261
 
1257
1262
  @dataclass(frozen=True)
@@ -1269,7 +1274,9 @@ def dedup_override_table(
1269
1274
  terminal_refs: dict[str, str] = {}
1270
1275
  for term_name, overrides in table.items():
1271
1276
  key = (tuple(overrides.narrower),
1272
- tuple(overrides.wider))
1277
+ tuple(overrides.wider),
1278
+ tuple(overrides.zeroer),
1279
+ tuple(overrides.narrow_zeroer))
1273
1280
  hash_key = hashlib.sha256(repr(key).encode()).hexdigest()[:8]
1274
1281
  if hash_key not in shared_sets:
1275
1282
  shared_sets[hash_key] = overrides
@@ -1405,11 +1412,14 @@ def make_single_override(
1405
1412
 
1406
1413
  Returns a dict mapping canonical_name to 'TerminalOverrides'.
1407
1414
 
1415
+ - 'zeroer' means terminal measured 0, but wcwidth measured 2,
1408
1416
  - 'narrower' means terminal measured 1, but wcwidth measured 2,
1409
1417
  - 'wider' means terminal measured 2, but wcwidth measured 1.
1410
1418
  """
1419
+ zeroer: dict[str, set[int]] = {}
1411
1420
  narrower: dict[str, set[int]] = {}
1412
1421
  wider: dict[str, set[int]] = {}
1422
+ narrow_zeroer_map: dict[str, set[int]] = {}
1413
1423
 
1414
1424
  for _, canonical, doc in load_ucs_detect_yaml():
1415
1425
  test_results = doc.get('test_results', {})
@@ -1420,7 +1430,11 @@ def make_single_override(
1420
1430
  ucs = parse_wchar_codepoint(wchar)
1421
1431
  term_w = entry['measured_by_terminal']
1422
1432
  wc_w = entry['measured_by_wcwidth']
1423
- if term_w == 1 and wc_w == 2:
1433
+ if term_w == 0 and wc_w == 2:
1434
+ zeroer.setdefault(canonical, set()).add(ucs)
1435
+ elif term_w == 0 and wc_w == 1 and category == 'narrow_results':
1436
+ narrow_zeroer_map.setdefault(canonical, set()).add(ucs)
1437
+ elif term_w == 1 and wc_w == 2:
1424
1438
  narrower.setdefault(canonical, set()).add(ucs)
1425
1439
  # 'wider' entries in emoji_vs16_results are from the vs16n baseline test
1426
1440
  # (base character measured without VS16, expected width 1). Kitty rendering
@@ -1431,11 +1445,14 @@ def make_single_override(
1431
1445
  wider.setdefault(canonical, set()).add(ucs)
1432
1446
 
1433
1447
  result: dict[str, TerminalOverrides] = {}
1434
- all_names = sorted(set(narrower.keys()) | set(wider.keys()))
1448
+ all_names = sorted(set(zeroer.keys()) | set(narrower.keys()) | set(wider.keys())
1449
+ | set(narrow_zeroer_map.keys()))
1435
1450
  for name in all_names:
1436
1451
  result[name] = TerminalOverrides(
1437
1452
  narrower=values_to_hex_ranges(narrower.get(name, set())),
1438
1453
  wider=values_to_hex_ranges(wider.get(name, set())),
1454
+ zeroer=values_to_hex_ranges(zeroer.get(name, set())),
1455
+ narrow_zeroer=values_to_hex_ranges(narrow_zeroer_map.get(name, set())),
1439
1456
  )
1440
1457
  result = {
1441
1458
  name: data for name, data in result.items()
@@ -1809,6 +1826,7 @@ def main(only_fetch: bool = False, fetch_all_versions: bool = False,
1809
1826
  _make_merged_category('SFZ_OVERRIDES', make_single_override('sfz_results', kt)),
1810
1827
  _make_merged_category('VS16_OVERRIDES', make_single_override('emoji_vs16_results', kt)),
1811
1828
  _make_merged_category('VS15_OVERRIDES', make_single_override('emoji_vs15_results', kt)),
1829
+ _make_merged_category('NARROW_OVERRIDES', make_single_override('narrow_results', kt)),
1812
1830
  ])
1813
1831
  yield from fetch_override_grapheme_data(kt)
1814
1832
  yield TermProgramTableRenderDef.new()
@@ -7,7 +7,7 @@ Terminals: {{ terminals|join(', ') }}
7
7
  """
8
8
  # pylint: skip-file
9
9
  GRAPHEMES = {
10
- {%- for grapheme_str, terminal_width in graphemes.items() %}
10
+ {%- for grapheme_str, terminal_width in graphemes | dictsort %}
11
11
  '{{ grapheme_str }}': {{ terminal_width }},
12
12
  {%- endfor %}
13
13
  }
@@ -5,7 +5,7 @@ This code generated by python wcwidth using ucs-detect project data.
5
5
  """
6
6
  # pylint: skip-file
7
7
  _REGISTRY = {
8
- {%- for terminal, hash_key in registry.items() %}
8
+ {%- for terminal, hash_key in registry | dictsort %}
9
9
  {{ terminal.__repr__() }}: {{ hash_key.__repr__() }},
10
10
  {%- endfor %}
11
11
  }
@@ -4,30 +4,33 @@ Terminal override tables keyed by canonical terminal software name.
4
4
  This code generated by python wcwidth using ucs-detect project data.
5
5
  """
6
6
  # pylint: skip-file
7
+ from __future__ import annotations
7
8
  {%- for category in categories %}
8
9
  {%- set varname = category.variable_name -%}
9
10
  {%- set shared_sets = category.shared_sets -%}
10
11
  {%- set terminal_refs = category.terminal_refs -%}
11
12
  {%- set set_terminals = category.set_terminals -%}
12
13
  {%- if shared_sets %}
13
- {%- for hash_key, overrides in shared_sets.items() %}
14
+ {%- for hash_key, overrides in shared_sets | dictsort %}
14
15
  {%- set terminals = set_terminals.get(hash_key, ()) %}
15
16
 
16
17
  # For terminals: {{ terminals | join(', ') }}
17
- _SET_{{ varname }}_{{ hash_key | upper }} = {
18
+ _SET_{{ varname }}_{{ hash_key | upper }}: dict[str, tuple[tuple[int, int], ...]] = {
18
19
  {%- for direction, ranges in overrides.items() %}
20
+ {%- if ranges %}
19
21
  '{{ direction }}': (
20
22
  {%- for hex_start, hex_end, txt_description in ranges %}
21
23
  ({{ hex_start }}, {{ hex_end }},), # {{ txt_description }}
22
24
  {%- endfor %}
23
25
  ),
26
+ {%- endif %}
24
27
  {%- endfor %}
25
28
  }
26
29
  {%- endfor %}
27
30
  {%- endif %}
28
31
 
29
- {{ varname }} = {
30
- {%- for term_name, hash_key in terminal_refs.items() %}
32
+ {{ varname }}: dict[str, dict[str, tuple[tuple[int, int], ...]]] = {
33
+ {%- for term_name, hash_key in terminal_refs | dictsort %}
31
34
  '{{ term_name }}': _SET_{{ varname }}_{{ hash_key | upper }},
32
35
  {%- endfor %}
33
36
  }
@@ -73,7 +73,7 @@ See also:
73
73
  - `Grapheme Clusters and Terminal Emulators`_
74
74
  - `terminal-unicode-core.tex`_
75
75
  - `State of Terminal Emulators in 2025`_
76
- - `Report of terminals supporting Graphemes (2027)`_
76
+ - `Perfecting Terminal Character Width Using Correction Tables (2026)`_
77
77
 
78
78
  The `jquast/ucs-detect`_ project publishes automatic results of compliance to our standard for Wide
79
79
  character, Languages, grapheme clustering, complex or combining scripts, emojis, zero-width joiner,
@@ -417,9 +417,10 @@ possible timeout, slow network, or non-response when working with "dumb terminal
417
417
  Corrections
418
418
  -----------
419
419
 
420
- Corrections are automatically applied depending on detected or given terminal software name
420
+ Corrections may be automatically applied depending on the detected or given terminal software name
421
421
  beginning with wcwidth release 0.8.0. This allows to correct widths for terminal software that
422
- differs from the standard. These corrections are sourced from the `jquast/ucs-detect`_ project.
422
+ differs from the python wcwidth specification_. These corrections are sourced from the
423
+ `jquast/ucs-detect`_ project.
423
424
 
424
425
  The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
425
426
  `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
@@ -653,10 +654,18 @@ languages:
653
654
  History
654
655
  =======
655
656
 
656
- 0.8.0 *(unreleased)*
657
+ 0.8.2 *2026-06-29*
658
+ * **Bugfix** Do not raise IndexError when given legacy POSIX ``n`` argument to `wcswidth()`_ or
659
+ `wcstwidth()`_ exceed string length without raising IndexError
660
+
661
+ 0.8.1 *2026-06-08*
662
+ * **Improved** `wcstwidth()`_ with new ``zeroer``, ``narrow_wider``, and ``narrow_zeroer``
663
+ Corrections_. `PR #226`_
664
+
665
+ 0.8.0 *2026-06-05*
657
666
  * **New** support for Variation Selector 15 Emojis as narrow, `Issue #211`_.
658
667
  * **New** argument, ``term_program`` for `wcstwidth()`_, `width()`_, `clip()`_, `wrap()`_,
659
- `ljust()`_, `rjust()`_, and `center()`_. ``False`` disables corrections; ``True``
668
+ `ljust()`_, `rjust()`_, and `center()`_. ``False`` disables Corrections_; ``True``
660
669
  auto-detects by ``TERM_PROGRAM`` or ``TERM``; string values accept canonical names matching
661
670
  `list_term_programs()`_. `wcstwidth()`_ defaults to ``True``; all other functions
662
671
  default to ``False``.
@@ -901,6 +910,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
901
910
  .. _`PR #221`: https://github.com/jquast/wcwidth/pull/221
902
911
  .. _`PR #223`: https://github.com/jquast/wcwidth/pull/223
903
912
  .. _`PR #224`: https://github.com/jquast/wcwidth/pull/224
913
+ .. _`PR #226`: https://github.com/jquast/wcwidth/pull/226
904
914
  .. _`Issue #101`: https://github.com/jquast/wcwidth/issues/101
905
915
  .. _`Issue #155`: https://github.com/jquast/wcwidth/issues/155
906
916
  .. _`Issue #190`: https://github.com/jquast/wcwidth/issues/190
@@ -980,7 +990,7 @@ https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::
980
990
  .. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
981
991
  .. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
982
992
  .. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
983
- .. _`Report of terminals supporting Graphemes (2027)`: https://ucs-detect.readthedocs.io/results.html#terminal-features
993
+ .. _`Perfecting Terminal Character Width Using Correction Tables (2026)`: https://www.jeffquast.com/post/perfecting-terminal-character-width-using-correction-tables/
984
994
  .. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
985
995
  .. _ENQ: https://documentation.help/PuTTY/config-answerback.html
986
996
  .. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
@@ -19,7 +19,7 @@ its docstring and specifications of related control codes, `XTerm Control Sequen
19
19
  Text Sizing Protocol`_.
20
20
 
21
21
  Each string yielded by :func:`wcwidth.iter_graphemes` may be mapped to :func:`wcwidth.wcswidth` to
22
- accurately measure the width of a **grapheme**. Although :func:`wcwidth.iter_graphemes` matches
22
+ accurately measure the width of a grapheme. Although :func:`wcwidth.iter_graphemes` matches
23
23
  behavior of Python 3.15 `uncodedata.iter_graphemes()`_ it differs in its return value,
24
24
  :func:`wcwidth.iter_graphemes` yields only strings, while :func:`unicodedata.iter_graphemes` yields
25
25
  ``unicodedata.Segment`` class objects.
@@ -33,7 +33,7 @@ The following have a column width of -1 for function :func:`wcwidth.wcwidth`
33
33
  - ``C1`` control characters and ``DEL`` (`U+007F`_ through `U+00A0`_).
34
34
 
35
35
  If any character in sequence contains ``C0`` or ``C1`` control characters, the final
36
- return value of of :func:`wcwidth.wcswidth` is -1.
36
+ return value of :func:`wcwidth.wcswidth` is -1.
37
37
 
38
38
  Width of 0
39
39
  ----------
@@ -4,7 +4,7 @@ requires = [ "hatchling" ]
4
4
 
5
5
  [project]
6
6
  name = "wcwidth"
7
- version = "0.8.0" # don't forget to also update wcwidth/__init__.py:__version__
7
+ version = "0.8.2" # don't forget to also update wcwidth/__init__.py:__version__
8
8
  description = "Measures the displayed width of unicode strings in a terminal"
9
9
  readme = "README.rst"
10
10
  keywords = [
@@ -497,6 +497,12 @@ def test_zwj_at_end_of_string():
497
497
  assert wcwidth.wcswidth('a\u200D') == 1
498
498
 
499
499
 
500
+ def test_wcswidth_n_exceeds_length():
501
+ """Verify wcswidth() with n > len(string) does not raise IndexError."""
502
+ assert wcwidth.wcswidth('hello', n=999) == 5
503
+ assert wcwidth.wcswidth('\u30B3\u30F3', n=999) == 4
504
+
505
+
500
506
  def test_soft_hyphen():
501
507
  # Test SOFT HYPHEN, category 'Cf' usually are zero-width, but most
502
508
  # implementations agree to draw it was '1' cell, visually
@@ -8,7 +8,11 @@ import pytest
8
8
  # local
9
9
  import wcwidth
10
10
  import wcwidth.table_grapheme_overrides as grapheme_overrides
11
- from wcwidth._constants import _merge_ranges, resolve_terminal, list_term_programs
11
+ from wcwidth._constants import (_EMPTY_OVERRIDES,
12
+ _merge_ranges,
13
+ resolve_terminal,
14
+ get_term_overrides,
15
+ list_term_programs)
12
16
  from wcwidth.table_overrides import VS15_OVERRIDES
13
17
 
14
18
 
@@ -362,6 +366,106 @@ def test_sfz_override_foot():
362
366
  assert wcwidth.wcstwidth('\U0001F3FB', term_program='foot') == 1
363
367
 
364
368
 
369
+ @pytest.mark.parametrize('term_program,expected', [
370
+ ('kitty', 0),
371
+ ('bobcat', 0),
372
+ (False, 2),
373
+ ])
374
+ def test_sfz_zeroer(term_program, expected):
375
+ """Standalone Fitzpatrick modifiers zeroed per terminal."""
376
+ assert wcwidth.wcswidth('\U0001F3FB') == 2
377
+ assert wcwidth.wcstwidth('\U0001F3FB', term_program=term_program) == expected
378
+
379
+
380
+ @pytest.mark.parametrize('kwargs,expected', [
381
+ ({}, 0),
382
+ ({'control_codes': 'ignore'}, 0),
383
+ ])
384
+ def test_width_zeroer(kwargs, expected):
385
+ """Width() zeroes standalone Fitzpatrick modifiers for kitty."""
386
+ assert wcwidth.width('\U0001F3FB', term_program='kitty', **kwargs) == expected
387
+
388
+
389
+ def test_empty_overrides_includes_zeroer():
390
+ """_EMPTY_OVERRIDES has six empty tuple fields."""
391
+ assert _EMPTY_OVERRIDES.narrower == ()
392
+ assert _EMPTY_OVERRIDES.vs16_narrower == ()
393
+ assert _EMPTY_OVERRIDES.vs15_wider == ()
394
+ assert _EMPTY_OVERRIDES.zeroer == ()
395
+ assert _EMPTY_OVERRIDES.narrow_wider == ()
396
+ assert _EMPTY_OVERRIDES.narrow_zeroer == ()
397
+
398
+
399
+ def test_get_term_overrides_returns_empty_when_no_overrides():
400
+ """get_term_overrides returns _EMPTY_OVERRIDES when terminal has no override data."""
401
+ get_term_overrides.cache_clear()
402
+ overrides = get_term_overrides('no-such-terminal')
403
+ assert overrides is _EMPTY_OVERRIDES
404
+
405
+
406
+ def test_get_term_overrides_reads_narrow_zeroer_key():
407
+ """get_term_overrides reads 'narrow_zeroer' key from NARROW_OVERRIDES."""
408
+ get_term_overrides.cache_clear()
409
+ overrides = get_term_overrides('kitty')
410
+ assert len(overrides.narrow_zeroer) == 9
411
+ assert overrides.narrow_zeroer[0] == (0x00AD, 0x00AD)
412
+
413
+
414
+ def test_get_term_overrides_narrow_wider_still_empty():
415
+ """get_term_overrides narrow_wider is empty when no 'wider' entries exist."""
416
+ get_term_overrides.cache_clear()
417
+ overrides = get_term_overrides('konsole')
418
+ assert overrides.narrow_wider == ()
419
+
420
+
421
+ @pytest.mark.parametrize('func', [wcwidth.wcstwidth, wcwidth.width])
422
+ def test_narrow_wider_width(func):
423
+ """Verify width() & wcstwidth() match result of narrow_wider overrides."""
424
+ assert wcwidth.wcswidth('\u261d') == 1
425
+ assert func('\u261d', term_program='kitty') == 2
426
+
427
+
428
+ @pytest.mark.parametrize('codepoint', [
429
+ '\u00ad',
430
+ '\u0600',
431
+ '\u0605',
432
+ '\u06dd',
433
+ '\u070f',
434
+ '\u0890',
435
+ '\u0891',
436
+ '\u08e2',
437
+ '\U000110bd',
438
+ '\U000110cd',
439
+ ])
440
+ def test_narrow_zeroer_cf_codepoints(codepoint):
441
+ """Cf format characters are zeroed by kitty/konsole/wezterm narrow_zeroer."""
442
+ assert wcwidth.wcswidth(codepoint) == 1
443
+ assert wcwidth.wcstwidth(codepoint, term_program='kitty') == 0
444
+ assert wcwidth.wcstwidth(codepoint, term_program='konsole') == 0
445
+ assert wcwidth.wcstwidth(codepoint, term_program='wezterm') == 0
446
+
447
+
448
+ @pytest.mark.parametrize('func', [wcwidth.wcstwidth, wcwidth.width])
449
+ def test_narrow_zeroer_not_applied_for_other_terminals(func):
450
+ """Terminals without narrow overrides keep width 1 for Cf characters."""
451
+ assert func('\u0600', term_program='xterm') == 1
452
+ assert func('\u0600', term_program='ghostty') == 1
453
+ assert func('\u0600', term_program='') == 1
454
+
455
+
456
+ @pytest.mark.parametrize('func,term_program,expected', [
457
+ (wcwidth.wcstwidth, 'kitty', 0),
458
+ (wcwidth.width, 'kitty', 0),
459
+ (wcwidth.wcstwidth, 'konsole', 0),
460
+ (wcwidth.width, 'konsole', 0),
461
+ (wcwidth.wcstwidth, 'wezterm', 0),
462
+ (wcwidth.width, 'wezterm', 0),
463
+ ])
464
+ def test_narrow_zeroer_width(func, term_program, expected):
465
+ """Width() matches wcstwidth() for narrow_zeroer overrides."""
466
+ assert func('\u0600', term_program=term_program) == expected
467
+
468
+
365
469
  @pytest.mark.parametrize('value,expected', [
366
470
  (' KITTY ', 'kitty'),
367
471
  (' ', None),
@@ -1,5 +1,5 @@
1
1
  ambiguous_width: 1
2
- datetime: 2026-06-05 16:16:32 UTC
2
+ datetime: 2026-06-08 04:40:36 UTC
3
3
  environment:
4
4
  COLORTERM: truecolor
5
5
  TERM: alacritty
@@ -8,40 +8,28 @@ preferred_encoding: UTF-8
8
8
  python_version: 3.14.5
9
9
  resource_profile:
10
10
  cpu_pct:
11
- - 18.0
12
- - 1.0
11
+ - 16.0
13
12
  - 0.0
14
- - 19.9
15
- - 41.9
16
- - 48.7
17
- - 51.9
18
- - 69.7
19
- - 45.9
13
+ - 1.0
14
+ - 63.9
15
+ - 78.9
20
16
  elapsed_s:
21
- - 1.001167821988929
22
- - 2.0029580629779957
23
- - 3.0050527160055935
24
- - 4.007474202953745
25
- - 5.009848670975771
26
- - 6.011714742984623
27
- - 7.0173555499641225
28
- - 8.019567951967474
29
- - 9.023896721948404
17
+ - 1.0009446729673073
18
+ - 2.0028591490117833
19
+ - 3.004715724964626
20
+ - 4.006955301039852
21
+ - 5.008106541004963
30
22
  hardware:
31
23
  cpu_count: 12
32
24
  cpu_model: AMD Ryzen 5 7640U w/ Radeon 760M Graphics
33
25
  ram_total_kb: 13961408
34
26
  rss_mb:
35
- - 120.61328125
36
- - 120.69921875
37
- - 120.69921875
38
- - 124.7734375
39
- - 127.66015625
40
- - 128.4296875
41
- - 130.4375
42
- - 130.90234375
43
- - 123.21484375
44
- seconds_elapsed: 4.96278640901437
27
+ - 120.69140625
28
+ - 120.69140625
29
+ - 120.69140625
30
+ - 130.140625
31
+ - 133.27734375
32
+ seconds_elapsed: 1.9186390159884468
45
33
  session_arguments:
46
34
  all: false
47
35
  limit_category_time: 0
@@ -8571,6 +8559,84 @@ test_results:
8571
8559
  n_errors: 0
8572
8560
  n_total: 8
8573
8561
  pct_success: 100.0
8562
+ narrow_results:
8563
+ 17.0.0:
8564
+ failed_codepoints:
8565
+ - measured_by_terminal: 0
8566
+ measured_by_wcwidth: 1
8567
+ wchar: \xad
8568
+ - measured_by_terminal: 0
8569
+ measured_by_wcwidth: 1
8570
+ wchar: \u0605
8571
+ - measured_by_terminal: 0
8572
+ measured_by_wcwidth: 1
8573
+ wchar: \u070f
8574
+ - measured_by_terminal: 0
8575
+ measured_by_wcwidth: 1
8576
+ wchar: \u0890
8577
+ - measured_by_terminal: 0
8578
+ measured_by_wcwidth: 1
8579
+ wchar: \u0891
8580
+ - measured_by_terminal: 0
8581
+ measured_by_wcwidth: 1
8582
+ wchar: \u08e2
8583
+ - measured_by_terminal: 0
8584
+ measured_by_wcwidth: 1
8585
+ wchar: \u0d4e
8586
+ - measured_by_terminal: 2
8587
+ measured_by_wcwidth: 1
8588
+ wchar: \u17a4
8589
+ - measured_by_terminal: 2
8590
+ measured_by_wcwidth: 1
8591
+ wchar: \u17d8
8592
+ - measured_by_terminal: 0
8593
+ measured_by_wcwidth: 1
8594
+ wchar: \ua8fa
8595
+ - measured_by_terminal: 0
8596
+ measured_by_wcwidth: 1
8597
+ wchar: \uff9e
8598
+ - measured_by_terminal: 0
8599
+ measured_by_wcwidth: 1
8600
+ wchar: \uff9f
8601
+ - measured_by_terminal: 0
8602
+ measured_by_wcwidth: 1
8603
+ wchar: \U000111c2
8604
+ - measured_by_terminal: 0
8605
+ measured_by_wcwidth: 1
8606
+ wchar: \U000111c3
8607
+ - measured_by_terminal: 0
8608
+ measured_by_wcwidth: 1
8609
+ wchar: \U0001193f
8610
+ - measured_by_terminal: 0
8611
+ measured_by_wcwidth: 1
8612
+ wchar: \U00011941
8613
+ - measured_by_terminal: 0
8614
+ measured_by_wcwidth: 1
8615
+ wchar: \U00011a84
8616
+ - measured_by_terminal: 0
8617
+ measured_by_wcwidth: 1
8618
+ wchar: \U00011a85
8619
+ - measured_by_terminal: 0
8620
+ measured_by_wcwidth: 1
8621
+ wchar: \U00011a86
8622
+ - measured_by_terminal: 0
8623
+ measured_by_wcwidth: 1
8624
+ wchar: \U00011a87
8625
+ - measured_by_terminal: 0
8626
+ measured_by_wcwidth: 1
8627
+ wchar: \U00011a88
8628
+ - measured_by_terminal: 0
8629
+ measured_by_wcwidth: 1
8630
+ wchar: \U00011a89
8631
+ - measured_by_terminal: 0
8632
+ measured_by_wcwidth: 1
8633
+ wchar: \U00011d46
8634
+ - measured_by_terminal: 0
8635
+ measured_by_wcwidth: 1
8636
+ wchar: \U00011f02
8637
+ n_errors: 24
8638
+ n_total: 187
8639
+ pct_success: 87.16577540106952
8574
8640
  ri_results:
8575
8641
  17.0.0:
8576
8642
  failed_codepoints: []
@@ -8749,7 +8815,7 @@ test_results:
8749
8815
  measured_by_wcwidth: 2
8750
8816
  wchar: \U0001f1ff
8751
8817
  n_errors: 26
8752
- n_total: 553
8753
- pct_success: 95.29837251356238
8754
- wcwidth_version: 0.8.0
8818
+ n_total: 506
8819
+ pct_success: 94.86166007905138
8820
+ wcwidth_version: 0.8.1
8755
8821
  width: 80
@@ -1,5 +1,5 @@
1
1
  ambiguous_width: 1
2
- datetime: 2026-06-05 16:19:11 UTC
2
+ datetime: 2026-06-08 04:39:53 UTC
3
3
  environment:
4
4
  COLORTERM: truecolor
5
5
  TERM: xterm-256color
@@ -9,15 +9,18 @@ python_version: 3.14.5
9
9
  resource_profile:
10
10
  cpu_pct:
11
11
  - 28.9
12
+ - 22.9
12
13
  elapsed_s:
13
- - 1.002688226988539
14
+ - 1.0030964689794928
15
+ - 2.0049722640542313
14
16
  hardware:
15
17
  cpu_count: 12
16
18
  cpu_model: AMD Ryzen 5 7640U w/ Radeon 760M Graphics
17
19
  ram_total_kb: 13961408
18
20
  rss_mb:
19
- - 122.2421875
20
- seconds_elapsed: 1.61378600995522
21
+ - 122.04296875
22
+ - 123.1015625
23
+ seconds_elapsed: 2.1441276310943067
21
24
  session_arguments:
22
25
  all: false
23
26
  limit_category_time: 0
@@ -9927,6 +9930,27 @@ test_results:
9927
9930
  n_errors: 0
9928
9931
  n_total: 8
9929
9932
  pct_success: 100.0
9933
+ narrow_results:
9934
+ 17.0.0:
9935
+ failed_codepoints:
9936
+ - measured_by_terminal: 0
9937
+ measured_by_wcwidth: 1
9938
+ wchar: \u0890
9939
+ - measured_by_terminal: 0
9940
+ measured_by_wcwidth: 1
9941
+ wchar: \u0891
9942
+ - measured_by_terminal: 0
9943
+ measured_by_wcwidth: 1
9944
+ wchar: \u08e2
9945
+ - measured_by_terminal: 0
9946
+ measured_by_wcwidth: 1
9947
+ wchar: \U000110bd
9948
+ - measured_by_terminal: 0
9949
+ measured_by_wcwidth: 1
9950
+ wchar: \U000110cd
9951
+ n_errors: 5
9952
+ n_total: 187
9953
+ pct_success: 97.32620320855615
9930
9954
  ri_results:
9931
9955
  17.0.0:
9932
9956
  failed_codepoints:
@@ -10774,7 +10798,7 @@ test_results:
10774
10798
  measured_by_wcwidth: 2
10775
10799
  wchar: \U0001f3ff
10776
10800
  n_errors: 11
10777
- n_total: 553
10778
- pct_success: 98.01084990958408
10779
- wcwidth_version: 0.8.0
10801
+ n_total: 506
10802
+ pct_success: 97.82608695652173
10803
+ wcwidth_version: 0.8.1
10780
10804
  width: 80