wcwidth 0.8.4__tar.gz → 0.9.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 (196) hide show
  1. {wcwidth-0.8.4 → wcwidth-0.9.0}/.gitignore +1 -0
  2. {wcwidth-0.8.4 → wcwidth-0.9.0}/.pylintrc +2 -1
  3. wcwidth-0.9.0/MANIFEST.in +16 -0
  4. {wcwidth-0.8.4 → wcwidth-0.9.0}/PKG-INFO +12 -13
  5. wcwidth-0.9.0/README.rst +557 -0
  6. wcwidth-0.9.0/bin/check-release.py +245 -0
  7. {wcwidth-0.8.4 → wcwidth-0.9.0}/bin/update-tables.py +7 -1
  8. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/api_c.rst +25 -26
  9. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/developing.rst +26 -5
  10. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/history.rst +11 -0
  11. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/intro.rst +5 -6
  12. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/libwcwidth.rst +30 -28
  13. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/specs.rst +2 -2
  14. wcwidth-0.9.0/libwcwidth/.clang-format +26 -0
  15. wcwidth-0.9.0/libwcwidth/CMakeLists.txt +117 -0
  16. wcwidth-0.9.0/libwcwidth/LICENSE +27 -0
  17. wcwidth-0.9.0/libwcwidth/Makefile +88 -0
  18. wcwidth-0.9.0/libwcwidth/README.rst +39 -0
  19. wcwidth-0.9.0/libwcwidth/cmake/libwcwidth-config.cmake.in +4 -0
  20. wcwidth-0.9.0/libwcwidth/examples/align.c +109 -0
  21. wcwidth-0.9.0/libwcwidth/examples/textwrap.c +176 -0
  22. wcwidth-0.9.0/libwcwidth/examples/width.c +116 -0
  23. wcwidth-0.9.0/libwcwidth/include/wcwidth/align.h +83 -0
  24. wcwidth-0.9.0/libwcwidth/include/wcwidth/clip.h +82 -0
  25. wcwidth-0.9.0/libwcwidth/include/wcwidth/escape.h +109 -0
  26. wcwidth-0.9.0/libwcwidth/include/wcwidth/grapheme.h +68 -0
  27. wcwidth-0.9.0/libwcwidth/include/wcwidth/sgr.h +71 -0
  28. wcwidth-0.9.0/libwcwidth/include/wcwidth/table_types.h +111 -0
  29. wcwidth-0.9.0/libwcwidth/include/wcwidth/tables.h +139 -0
  30. wcwidth-0.9.0/libwcwidth/include/wcwidth/terminal_override.h +59 -0
  31. wcwidth-0.9.0/libwcwidth/include/wcwidth/text_sizing.h +58 -0
  32. wcwidth-0.9.0/libwcwidth/include/wcwidth/textwrap.h +101 -0
  33. wcwidth-0.9.0/libwcwidth/include/wcwidth/unicode.h +63 -0
  34. wcwidth-0.9.0/libwcwidth/include/wcwidth/utf8.h +61 -0
  35. wcwidth-0.9.0/libwcwidth/include/wcwidth/wcwidth.h +62 -0
  36. wcwidth-0.9.0/libwcwidth/include/wcwidth/wcwidth_config.h +37 -0
  37. wcwidth-0.9.0/libwcwidth/include/wcwidth/width.h +76 -0
  38. wcwidth-0.9.0/libwcwidth/src/align.c +371 -0
  39. wcwidth-0.9.0/libwcwidth/src/bisearch.c +38 -0
  40. wcwidth-0.9.0/libwcwidth/src/clip.c +544 -0
  41. wcwidth-0.9.0/libwcwidth/src/escape.c +613 -0
  42. wcwidth-0.9.0/libwcwidth/src/grapheme.c +676 -0
  43. wcwidth-0.9.0/libwcwidth/src/sgr.c +595 -0
  44. wcwidth-0.9.0/libwcwidth/src/tables/table_ambiguous.c +189 -0
  45. wcwidth-0.9.0/libwcwidth/src/tables/table_gcb_class.c +972 -0
  46. wcwidth-0.9.0/libwcwidth/src/tables/table_grapheme.c +2401 -0
  47. wcwidth-0.9.0/libwcwidth/src/tables/table_mc.c +209 -0
  48. wcwidth-0.9.0/libwcwidth/src/tables/table_term_programs.c +24 -0
  49. wcwidth-0.9.0/libwcwidth/src/tables/table_terminal_overrides.c +37735 -0
  50. wcwidth-0.9.0/libwcwidth/src/tables/table_vs15.c +104 -0
  51. wcwidth-0.9.0/libwcwidth/src/tables/table_vs16.c +126 -0
  52. wcwidth-0.9.0/libwcwidth/src/tables/table_wide.c +141 -0
  53. wcwidth-0.9.0/libwcwidth/src/tables/table_zero.c +356 -0
  54. wcwidth-0.9.0/libwcwidth/src/terminal_override.c +235 -0
  55. wcwidth-0.9.0/libwcwidth/src/text_sizing.c +208 -0
  56. wcwidth-0.9.0/libwcwidth/src/textwrap.c +1183 -0
  57. wcwidth-0.9.0/libwcwidth/src/utf8.c +249 -0
  58. wcwidth-0.9.0/libwcwidth/src/wcstwidth.c +325 -0
  59. wcwidth-0.9.0/libwcwidth/src/wcswidth.c +195 -0
  60. wcwidth-0.9.0/libwcwidth/src/wcwidth.c +38 -0
  61. wcwidth-0.9.0/libwcwidth/src/width.c +1814 -0
  62. wcwidth-0.9.0/libwcwidth/tests/test_align.c +251 -0
  63. wcwidth-0.9.0/libwcwidth/tests/test_clip.c +158 -0
  64. wcwidth-0.9.0/libwcwidth/tests/test_common.h +84 -0
  65. wcwidth-0.9.0/libwcwidth/tests/test_escape.c +156 -0
  66. wcwidth-0.9.0/libwcwidth/tests/test_gcb_class.c +116 -0
  67. wcwidth-0.9.0/libwcwidth/tests/test_grapheme.c +205 -0
  68. wcwidth-0.9.0/libwcwidth/tests/test_sgr.c +175 -0
  69. wcwidth-0.9.0/libwcwidth/tests/test_text_sizing.c +49 -0
  70. wcwidth-0.9.0/libwcwidth/tests/test_textwrap.c +255 -0
  71. wcwidth-0.9.0/libwcwidth/tests/test_utf8.c +58 -0
  72. wcwidth-0.9.0/libwcwidth/tests/test_wcswidth.c +101 -0
  73. wcwidth-0.9.0/libwcwidth/tests/test_wcwidth.c +21 -0
  74. wcwidth-0.9.0/libwcwidth/tests/test_width.c +192 -0
  75. wcwidth-0.9.0/pyproject.toml +93 -0
  76. wcwidth-0.9.0/requirements-wheels.in +3 -0
  77. wcwidth-0.9.0/requirements-wheels.txt +40 -0
  78. wcwidth-0.9.0/setup.cfg +4 -0
  79. wcwidth-0.9.0/setup.py +167 -0
  80. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/conftest.py +17 -0
  81. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_benchmarks.py +0 -11
  82. wcwidth-0.9.0/tests/test_check_release.py +64 -0
  83. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_clip.py +387 -0
  84. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_core.py +138 -11
  85. wcwidth-0.9.0/tests/test_setup.py +90 -0
  86. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_textwrap.py +10 -6
  87. {wcwidth-0.8.4 → wcwidth-0.9.0}/tox.ini +83 -8
  88. wcwidth-0.9.0/wcwidth/__init__.py +101 -0
  89. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/_clip.py +52 -22
  90. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/_wcswidth.py +4 -4
  91. wcwidth-0.9.0/wcwidth/_wcwidth_c.c +1146 -0
  92. wcwidth-0.9.0/wcwidth/_wcwidth_c.pyi +77 -0
  93. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/_width.py +4 -5
  94. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/align.py +6 -6
  95. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/escape_sequences.py +2 -2
  96. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/grapheme.py +1 -1
  97. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/hyperlink.py +1 -1
  98. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/textwrap.py +18 -20
  99. wcwidth-0.9.0/wcwidth.egg-info/PKG-INFO +588 -0
  100. wcwidth-0.9.0/wcwidth.egg-info/SOURCES.txt +186 -0
  101. wcwidth-0.9.0/wcwidth.egg-info/dependency_links.txt +1 -0
  102. wcwidth-0.9.0/wcwidth.egg-info/top_level.txt +1 -0
  103. wcwidth-0.8.4/README.rst +0 -1
  104. wcwidth-0.8.4/bin/show-sequences +0 -23
  105. wcwidth-0.8.4/bin/strip-sequences +0 -18
  106. wcwidth-0.8.4/docs/requirements.txt +0 -62
  107. wcwidth-0.8.4/pyproject.toml +0 -72
  108. wcwidth-0.8.4/requirements-tests38.in +0 -5
  109. wcwidth-0.8.4/requirements-tests38.txt +0 -32
  110. wcwidth-0.8.4/wcwidth/__init__.py +0 -79
  111. {wcwidth-0.8.4 → wcwidth-0.9.0}/LICENSE +0 -0
  112. {wcwidth-0.8.4 → wcwidth-0.9.0}/bin/update-docs.py +0 -0
  113. {wcwidth-0.8.4 → wcwidth-0.9.0}/bin/verify-table-integrity.py +0 -0
  114. {wcwidth-0.8.4 → wcwidth-0.9.0}/bin/wcwidth-browser.py +0 -0
  115. {wcwidth-0.8.4 → wcwidth-0.9.0}/bin/wcwidth-libc-comparator.py +0 -0
  116. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/api_c.rst.j2 +0 -0
  117. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/c_gcb_class.c.j2 +0 -0
  118. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/c_table.c.j2 +0 -0
  119. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/c_term_programs.c.j2 +0 -0
  120. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/c_terminal_overrides.c.j2 +0 -0
  121. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/grapheme_override_per_terminal.py.j2 +0 -0
  122. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/grapheme_override_table.py.j2 +0 -0
  123. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/grapheme_registry.py.j2 +0 -0
  124. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/grapheme_table.c.j2 +0 -0
  125. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/grapheme_table.py.j2 +0 -0
  126. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/python_table.py.j2 +0 -0
  127. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/table_overrides.py.j2 +0 -0
  128. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/tables.h.j2 +0 -0
  129. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/term_programs.py.j2 +0 -0
  130. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/unicode_version.rst.j2 +0 -0
  131. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/unicode_versions.py.j2 +0 -0
  132. {wcwidth-0.8.4 → wcwidth-0.9.0}/code_templates/wcwidth_config.h.j2 +0 -0
  133. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/api.rst +0 -0
  134. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/conf.py +0 -0
  135. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/index.rst +0 -0
  136. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/related.rst +0 -0
  137. {wcwidth-0.8.4 → wcwidth-0.9.0}/docs/unicode_version.rst +0 -0
  138. {wcwidth-0.8.4 → wcwidth-0.9.0}/requirements-develop.txt +0 -0
  139. {wcwidth-0.8.4 → wcwidth-0.9.0}/requirements-docs.in +0 -0
  140. {wcwidth-0.8.4 → wcwidth-0.9.0}/requirements-tests39.in +0 -0
  141. {wcwidth-0.8.4 → wcwidth-0.9.0}/requirements-tests39.txt +0 -0
  142. {wcwidth-0.8.4 → wcwidth-0.9.0}/requirements-update.in +0 -0
  143. {wcwidth-0.8.4 → wcwidth-0.9.0}/requirements-update.txt +0 -0
  144. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/__init__.py +0 -0
  145. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_ambiguous.py +0 -0
  146. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_clip_cjk_emoji.py +0 -0
  147. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_clip_overtyping.py +0 -0
  148. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_emojis.py +0 -0
  149. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_grapheme.py +0 -0
  150. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_hyperlink.py +0 -0
  151. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_justify.py +0 -0
  152. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_sgr_state.py +0 -0
  153. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_term_overrides.py +0 -0
  154. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_text_sizing.py +0 -0
  155. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_ucslevel.py +0 -0
  156. {wcwidth-0.8.4 → wcwidth-0.9.0}/tests/test_width.py +0 -0
  157. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/_constants.py +0 -0
  158. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/_wcwidth.py +0 -0
  159. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/bisearch.py +0 -0
  160. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/control_codes.py +0 -0
  161. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/py.typed +0 -0
  162. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/sgr_state.py +0 -0
  163. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_ambiguous.py +0 -0
  164. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme.py +0 -0
  165. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/__init__.py +0 -0
  166. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_058c7585.py +0 -0
  167. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_2148ff29.py +0 -0
  168. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_244dc88b.py +0 -0
  169. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_336a71a1.py +0 -0
  170. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_33ac73f2.py +0 -0
  171. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_3a1895d5.py +0 -0
  172. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_59180a0d.py +0 -0
  173. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_65c6beb5.py +0 -0
  174. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_78f828e8.py +0 -0
  175. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_8c2ae14d.py +0 -0
  176. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_900356bf.py +0 -0
  177. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_97904ece.py +0 -0
  178. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_a5880eeb.py +0 -0
  179. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_b6d33cf4.py +0 -0
  180. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_c57c295b.py +0 -0
  181. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_cf02297d.py +0 -0
  182. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_e18ac6a7.py +0 -0
  183. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_e2b75ee3.py +0 -0
  184. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_fb5fd79b.py +0 -0
  185. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_known_fdc99132.py +0 -0
  186. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_grapheme_overrides/_registry.py +0 -0
  187. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_mc.py +0 -0
  188. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_overrides.py +0 -0
  189. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_term_programs.py +0 -0
  190. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_vs15.py +0 -0
  191. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_vs16.py +0 -0
  192. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_wide.py +0 -0
  193. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/table_zero.py +0 -0
  194. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/text_sizing.py +0 -0
  195. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/unicode_versions.py +0 -0
  196. {wcwidth-0.8.4 → wcwidth-0.9.0}/wcwidth/wcwidth.py +0 -0
@@ -8,6 +8,7 @@ __pycache__
8
8
  *.pyc
9
9
  *.swp
10
10
  build
11
+ wheelhouse
11
12
  /libwcwidth/build-cmake
12
13
  /libwcwidth/core
13
14
  *.so
@@ -9,12 +9,13 @@ load-plugins=
9
9
  persistent = no
10
10
  jobs = 0
11
11
  unsafe-load-any-extension = yes
12
- good-names = wc,fp
12
+ good-names = wc,fp,HAS_C_EXTENSION
13
13
 
14
14
  [MESSAGES CONTROL]
15
15
  disable=
16
16
  I,
17
17
  fixme,
18
+ import-error, # optional C extension is not built in lint environments
18
19
  c-extension-no-member,
19
20
  ungrouped-imports,
20
21
  useless-object-inheritance,
@@ -0,0 +1,16 @@
1
+ # Whitelist of contents of 'sdist'
2
+ include LICENSE README.rst tox.ini .gitignore .pylintrc
3
+ include requirements-*.in requirements-*.txt
4
+ recursive-include bin *.py
5
+ recursive-include code_templates *.j2 *.py
6
+ recursive-include docs *.rst *.py
7
+ recursive-include tests *.py
8
+ recursive-include wcwidth *.py *.pyi py.typed
9
+ recursive-include libwcwidth/src *.c *.h
10
+ recursive-include libwcwidth/include *.h
11
+ recursive-include libwcwidth/tests *.c *.h
12
+ recursive-include libwcwidth/examples *.c
13
+ recursive-include libwcwidth/cmake *.in
14
+ include libwcwidth/CMakeLists.txt libwcwidth/Makefile libwcwidth/README.rst libwcwidth/LICENSE
15
+ include libwcwidth/.clang-format
16
+
@@ -1,19 +1,17 @@
1
- Metadata-Version: 2.5
1
+ Metadata-Version: 2.4
2
2
  Name: wcwidth
3
- Version: 0.8.4
3
+ Version: 0.9.0
4
4
  Summary: Measures the displayed width of unicode strings in a terminal
5
- Project-URL: Homepage, https://github.com/jquast/wcwidth
6
5
  Author-email: Jeff Quast <contact@jeffquast.com>
7
- License-Expression: MIT
8
- License-File: LICENSE
6
+ Project-URL: Homepage, https://github.com/jquast/wcwidth
9
7
  Keywords: cjk,combining,console,eastasian,emoji,emulator,terminal,unicode,wcswidth,wcwidth,xterm
10
8
  Classifier: Development Status :: 5 - Production/Stable
11
9
  Classifier: Environment :: Console
12
10
  Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
13
12
  Classifier: Natural Language :: English
14
13
  Classifier: Operating System :: POSIX
15
14
  Classifier: Programming Language :: Python :: 3 :: Only
16
- Classifier: Programming Language :: Python :: 3.8
17
15
  Classifier: Programming Language :: Python :: 3.9
18
16
  Classifier: Programming Language :: Python :: 3.10
19
17
  Classifier: Programming Language :: Python :: 3.11
@@ -26,8 +24,10 @@ Classifier: Topic :: Software Development :: Libraries
26
24
  Classifier: Topic :: Software Development :: Localization
27
25
  Classifier: Topic :: Terminals
28
26
  Classifier: Typing :: Typed
29
- Requires-Python: >=3.8
27
+ Requires-Python: >=3.9
30
28
  Description-Content-Type: text/x-rst
29
+ License-File: LICENSE
30
+ Dynamic: license-file
31
31
 
32
32
  |pypi_downloads| |codecov| |license|
33
33
 
@@ -35,9 +35,8 @@ Description-Content-Type: text/x-rst
35
35
  Introduction
36
36
  ============
37
37
 
38
- This Python library is mainly for CLI/TUI programs that carefully produce output for Terminals.
39
-
40
- See page libwcwidth_ about the portable C11 library.
38
+ This Python and C11 library is mainly for CLI/TUI programs that carefully produce output for
39
+ Terminals. This page is about the Python library, see libwcwidth_ for the portable C11 library.
41
40
 
42
41
  Installation
43
42
  ------------
@@ -61,9 +60,9 @@ Some examples of **incorrect results**:
61
60
  >>> 'コンニチハ'.rjust(11, 'X')
62
61
  'XXXXXXコンニチハ'
63
62
 
64
- >>> # result consumes 5 total cells, 6 expected,
65
- >>> 'café'.center(6, 'X')
66
- 'caféX'
63
+ >>> # combining acute accent: result consumes 5 total cells, 6 expected,
64
+ >>> 'cafe\u0301'.center(6, 'X')
65
+ 'caféX'
67
66
 
68
67
  Solution
69
68
  --------
@@ -0,0 +1,557 @@
1
+ |pypi_downloads| |codecov| |license|
2
+
3
+ ============
4
+ Introduction
5
+ ============
6
+
7
+ This Python and C11 library is mainly for CLI/TUI programs that carefully produce output for
8
+ Terminals. This page is about the Python library, see libwcwidth_ for the portable C11 library.
9
+
10
+ Installation
11
+ ------------
12
+
13
+ The stable version of this package is maintained on pypi, install or upgrade, using pip::
14
+
15
+ pip install --upgrade wcwidth
16
+
17
+ Problem
18
+ -------
19
+
20
+ All Python string-formatting functions, `textwrap.wrap()`_, `str.ljust()`_, `str.rjust()`_, and
21
+ `str.center()`_ **incorrectly** measure the displayed width of a string as equal to the number of
22
+ their codepoints.
23
+
24
+ Some examples of **incorrect results**:
25
+
26
+ .. code-block:: python
27
+
28
+ >>> # result consumes 16 total cells, 11 expected,
29
+ >>> 'コンニチハ'.rjust(11, 'X')
30
+ 'XXXXXXコンニチハ'
31
+
32
+ >>> # combining acute accent: result consumes 5 total cells, 6 expected,
33
+ >>> 'cafe\u0301'.center(6, 'X')
34
+ 'caféX'
35
+
36
+ Solution
37
+ --------
38
+
39
+ The lowest-level functions in this library are derived from POSIX.1-2001 and POSIX.1-2008
40
+ `wcwidth(3)`_ and `wcswidth(3)`_, which this library precisely copies by interface as `wcwidth()`_
41
+ and `wcswidth()`_. These functions return -1 when C0 and C1 control codes are present.
42
+
43
+ An easy-to-use `width()`_ function is provided as a wrapper of `wcswidth()`_ that is also capable of
44
+ measuring most terminal control codes and sequences, like colors, bold, tabstops, and horizontal
45
+ cursor movement. `width()`_ argument ``term_program`` may provide more accurate terminal measurement
46
+ Corrections_ as a wrapper of `wcstwidth()`_.
47
+
48
+ Text-justification is solved by the sequence-aware functions `ljust()`_, `rjust()`_, `center()`_,
49
+ and the grapheme-aware function `wrap()`_, serving as drop-in replacements to python standard
50
+ functions.
51
+
52
+ The `clip()`_ function extracts substrings by their displayed column positions, and
53
+ `strip_sequences()`_ removes terminal escape sequences from text altogether.
54
+
55
+ The iterator functions `iter_graphemes()`_ and `iter_sequences()`_ allow for careful navigation of
56
+ grapheme and terminal control sequence boundaries as required by editors or REPLs with cursor
57
+ control. `iter_graphemes_reverse()`_ and `grapheme_boundary_before()`_ are necessary for backward
58
+ cursor control over complex unicode.
59
+
60
+ Discrepancies
61
+ -------------
62
+
63
+ You may find that support *varies* for complex unicode sequences or codepoints.
64
+
65
+ This library may be considered to presume the terminal is enabled for DEC Private Mode 2027
66
+ ("Grapheme Clustering") by default, which may require to be enabled by a TUI application but
67
+ is often the default mode for those terminals that support it.
68
+
69
+ This library does support any specific "legacy width" measurement by API, but it does provide
70
+ Corrections_ for those terminals without grapheme support.
71
+
72
+ See also:
73
+
74
+ - `terminal-unicode-core.tex`_ (2021)
75
+ - `Grapheme Clusters and Terminal Emulators`_ (2023)
76
+ - `State of Terminal Emulators in 2025`_
77
+ - `Perfecting Terminal Character Width Using Correction Tables`_ (2026)
78
+
79
+ The `jquast/ucs-detect`_ project publishes automatic results of compliance to our standard for Wide
80
+ character, Languages, grapheme clustering, complex or combining scripts, emojis, zero-width joiner,
81
+ variations, and regional indicator (flags) as a `General Tabulated Summary`_ by terminal emulator
82
+ software and version. The results of the ucs-detect project create our correction tables.
83
+
84
+ ========
85
+ Overview
86
+ ========
87
+
88
+ A brief overview, through examples, for all of the public API functions.
89
+
90
+ Full API Documentation at https://wcwidth.readthedocs.io/en/latest/api.html
91
+
92
+ wcwidth()
93
+ ---------
94
+
95
+ Measures width of a single codepoint,
96
+
97
+ .. code-block:: python
98
+
99
+ >>> # '♀' narrow emoji
100
+ >>> wcwidth.wcwidth('\u2640')
101
+ 1
102
+
103
+ Use function `wcwidth()`_ to determine the length of a *single unicode character*.
104
+
105
+ See specification_ of character measurements. Note that ``-1`` is returned for control codes.
106
+
107
+ wcswidth()
108
+ ----------
109
+
110
+ Measures width of a string, returns -1 for control codes.
111
+
112
+ .. code-block:: python
113
+
114
+ >>> # '♀️' emoji w/vs-16
115
+ >>> wcwidth.wcswidth('\u2640\ufe0f')
116
+ 2
117
+
118
+ Use function `wcswidth()`_ to determine the length of many, a *string of unicode characters*.
119
+
120
+ See specification_ of character measurements. Note that ``-1`` is returned if control codes occurs
121
+ anywhere in the string.
122
+
123
+ wcstwidth()
124
+ -----------
125
+
126
+ Same behavior as `wcswidth()`_ with automatic terminal-specific Corrections_, reading
127
+ ``TERM_PROGRAM`` or ``TERM`` when ``True`` (default), or caller can provide terminal query
128
+ XTVERSION_ or ENQ_ response:
129
+
130
+ .. code-block:: python
131
+
132
+ >>> # '♀️' emoji w/vs-16, uncorrected:
133
+ >>> wcwidth.wcswidth('\u2640\ufe0f')
134
+ 2
135
+ >>> # corrected,
136
+ >>> wcwidth.wcstwidth('\u2640\ufe0f', term_program='vte')
137
+ 1
138
+
139
+ width()
140
+ -------
141
+
142
+ Use function `width()`_ to measure a string with improved handling of ``control_codes`` and
143
+ measurement Corrections_ through ``term_program``:
144
+
145
+ .. code-block:: python
146
+
147
+ >>> # same support as wcswidth(), eg. regional indicator flag:
148
+ >>> wcwidth.width('\U0001F1FF\U0001F1FC')
149
+ 2
150
+ >>> # set term_program=True to use wcstwidth()
151
+ >>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program=True)
152
+ 1
153
+ >>> # or set term_program for measurement of a specific terminal
154
+ >>> wcwidth.width('\U0001F1FF\U0001F1FC', term_program='contour')
155
+ 2
156
+ >>> # but also supports sequences, like SGR colored text, "WARN", followed by reset
157
+ >>> wcwidth.width('\x1b[38;2;255;150;100mWARN\x1b[0m')
158
+ 4
159
+ >>> # tabs are measured as though the string begins at a tabstop,
160
+ >>> wcwidth.width('\t', tabsize=4)
161
+ 4
162
+ >>> # or, all control characters can be ignored (including tab)
163
+ >>> wcwidth.width('\t\n\a\r', control_codes='ignore')
164
+ 0
165
+ >>> # sequences with "indeterminate" effects like Home + Clear are zero-width
166
+ >>> wcwidth.width('\x1b[H\x1b[2J')
167
+ 0
168
+ >>> # horizontal cursor movements are parsed,
169
+ >>> wcwidth.width('hello\b\b\b\b\bworld')
170
+ 5
171
+ >>> wcwidth.width('hello\x1b[5Dworld')
172
+ 5
173
+ >>> # or ignored,
174
+ >>> wcwidth.width('hello\x1b[5Dworld', control_codes='ignore')
175
+ 10
176
+ >>> # Measure width of text using kitty text sizing protocol (OSC 66),
177
+ >>> width('\x1b]66;w=2;XY\x07')
178
+ 2
179
+ >>> # Scaled text sizing: each grapheme occupies 'scale' cells
180
+ >>> width('\x1b]66;s=2;ABC\x07')
181
+ 6
182
+
183
+ Use ``control_codes='ignore'`` when the input is known not to contain any control characters or
184
+ terminal sequences for slightly improved performance. Note that TAB (``'\t'``) is a control
185
+ character and is also ignored, you may want to use `str.expandtabs()`_, first.
186
+
187
+ Use ``control_codes='strict'`` when input is known to contain some control sequences, such as
188
+ SGR color, bold, hyperlinks and cursor movement. Any sequence that cannot be accurately parsed
189
+ for horizontal measurement, such as clearing the screen, vertical, or absolute cursor movement will
190
+ raise ``ValueError``:
191
+
192
+ .. code-block:: python
193
+
194
+ >>> # or, raise ValueError for "indeterminate" effects using control_codes='strict'
195
+ >>> wcwidth.width('\n', control_codes='strict')
196
+ Traceback (most recent call last):
197
+ ...
198
+ ValueError: Vertical movement character 0xa at position 0
199
+
200
+
201
+ >>> wcwidth.width('\x1b[H\x1b[2J', control_codes='strict')
202
+ Traceback (most recent call last):
203
+ ...
204
+ ValueError: Indeterminate cursor sequence at position 0, '\x1b[H'
205
+
206
+
207
+ >>> # cursor left movement beyond string start raises in strict mode,
208
+ >>> wcwidth.width('a\x1b[5Da', control_codes='strict')
209
+ Traceback (most recent call last):
210
+ ...
211
+ ValueError: Cursor left movement at position 1 would move 5 cells left from column 1, exceeding string start
212
+
213
+ iter_sequences()
214
+ ----------------
215
+
216
+ Iterates through text, segmented by terminal sequence,
217
+
218
+ .. code-block:: python
219
+
220
+ >>> list(wcwidth.iter_sequences('hello'))
221
+ [('hello', False)]
222
+ >>> list(wcwidth.iter_sequences('\x1b[31mred\x1b[0m'))
223
+ [('\x1b[31m', True), ('red', False), ('\x1b[0m', True)]
224
+
225
+ Use `iter_sequences()`_ to split text into segments of plain text and escape sequences. Each tuple
226
+ contains the segment string and a boolean indicating whether it is an escape sequence (``True``) or
227
+ text (``False``).
228
+
229
+ iter_graphemes()
230
+ ----------------
231
+
232
+ Use `iter_graphemes()`_ to iterate over *grapheme clusters* of a string.
233
+
234
+ .. code-block:: python
235
+
236
+ >>> from wcwidth import iter_graphemes
237
+ >>> # ok + Regional Indicator 'Z', 'W' (Zimbabwe)
238
+ >>> list(wcwidth.iter_graphemes('ok\U0001F1FF\U0001F1FC'))
239
+ ['o', 'k', '🇿🇼']
240
+
241
+ >>> # cafe + combining acute accent
242
+ >>> list(wcwidth.iter_graphemes('cafe\u0301'))
243
+ ['c', 'a', 'f', 'é']
244
+
245
+ >>> # ok + Emoji Man + ZWJ + Woman + ZWJ + Girl
246
+ >>> list(wcwidth.iter_graphemes('ok\U0001F468\u200D\U0001F469\u200D\U0001F467'))
247
+ ['o', 'k', '👨\u200d👩\u200d👧']
248
+
249
+ A grapheme cluster is what a user perceives as a single character, even if it is composed of
250
+ multiple Unicode codepoints. This function implements `Unicode Standard Annex #29`_ grapheme cluster
251
+ boundary rules.
252
+
253
+ ljust()
254
+ -------
255
+
256
+ Use `ljust()`_ as replacement of `str.ljust()`_:
257
+
258
+ .. code-block:: python
259
+
260
+ >>> 'コンニチハ'.ljust(11, '*') # don't do this
261
+ 'コンニチハ******'
262
+ >>> wcwidth.ljust('コンニチハ', 11, '*') # do this!
263
+ 'コンニチハ*'
264
+
265
+ rjust()
266
+ -------
267
+
268
+ Use `rjust()`_ as replacement of `str.rjust()`_:
269
+
270
+ .. code-block:: python
271
+
272
+ >>> 'コンニチハ'.rjust(11, '*') # don't do this
273
+ '******コンニチハ'
274
+ >>> wcwidth.rjust('コンニチハ', 11, '*') # do this!
275
+ '*コンニチハ'
276
+
277
+ center()
278
+ --------
279
+
280
+ Use `center()`_ as replacement of `str.center()`_:
281
+
282
+ .. code-block:: python
283
+
284
+ >>> 'cafe\u0301'.center(6, '*') # don't do this
285
+ 'café*'
286
+ >>> wcwidth.center('cafe\u0301', 6, '*')
287
+ '*café*' # do this!
288
+
289
+ wrap()
290
+ ------
291
+
292
+ Use function `wrap()`_ to wrap text containing terminal sequences, Unicode grapheme
293
+ clusters, and wide characters to a given display width.
294
+
295
+ .. code-block:: python
296
+
297
+ >>> from wcwidth import wrap
298
+ >>> # Basic wrapping
299
+ >>> wrap('hello world', 5)
300
+ ['hello', 'world']
301
+
302
+ >>> # Wrapping CJK text (each character is 2 cells wide)
303
+ >>> wrap('コンニチハ', 4)
304
+ ['コン', 'ニチ', 'ハ']
305
+
306
+ >>> # Text with ANSI color sequences - SGR codes are propagated by default
307
+ >>> # Each line ends with reset, next line starts with restored style
308
+ >>> wrap('\x1b[1;31mhello world\x1b[0m', 5)
309
+ ['\x1b[1;31mhello\x1b[0m', '\x1b[1;31mworld\x1b[0m']
310
+
311
+ clip()
312
+ ------
313
+
314
+ Use `clip()`_ to extract a substring by column positions, preserving terminal sequences.
315
+
316
+ .. code-block:: python
317
+
318
+ >>> from wcwidth import clip
319
+ >>> # Wide characters split to Narrow boundaries using fillchar=' '
320
+ >>> clip('中文字', 0, 3)
321
+ '中 '
322
+ >>> clip('中文字', 1, 5, fillchar='.')
323
+ '.文.'
324
+
325
+ >>> # 'end' defaults to -1, meaning "to the end of the line"
326
+ >>> clip('中文字', 1)
327
+ ' 文字'
328
+ >>> clip('\x1b[1;31mHello world\x1b[0m', 6)
329
+ '\x1b[1;31mworld\x1b[0m'
330
+
331
+ >>> # SGR codes are propagated by default - result begins with active style
332
+ >>> # and ends with reset if styles are active
333
+ >>> clip('\x1b[1;31mHello world\x1b[0m', 6, 11)
334
+ '\x1b[1;31mworld\x1b[0m'
335
+
336
+ >>> # Disable SGR propagation to preserve sequence order outside of clip boundary
337
+ >>> clip('\x1b[31m中文\x1b[32m', 0, 3, propagate_sgr=False)
338
+ '\x1b[31m中 \x1b[32m'
339
+
340
+ >>> # Cursor-left overwrites previous text (painter's algorithm)
341
+ >>> clip('hello\x1b[2DXY', 0, 5)
342
+ 'helXY'
343
+ >>> # Carriage return resets to column 0, overwriting earlier cells
344
+ >>> clip('abc\rXY', 0, 5)
345
+ 'XYc'
346
+
347
+ >>> # even OSC 8 hyperlink text may be clipped, 'Click This link' -> 'is link' !
348
+ >>> clip('\x1b]8;;http://example.com\x07Click This link\x1b]8;;\x07', 8, 15)
349
+ '\x1b]8;;http://example.com\x07is link\x1b]8;;\x07'
350
+
351
+ >>> # and OSC 66 kitty text sizing, supporting width and scale, 'Look' -> '...ook'
352
+ >>> clip('\x1b]66;w=4:s=4;Look\x07', 1, 16, fillchar='.')
353
+ '...\x1b]66;s=4:w=3;ook\x07'
354
+
355
+ Use ``overtyping=False`` when the input is known not to contain any cursor movement characters
356
+ (``\b``, ``\r``, ``CSI C``, ``CSI D``, ``CSI G``) for improved performance. When
357
+ ``overtyping=None`` (default), a slower "Painter's algorithm" may be used after testing for the
358
+ presence of these characters. ``overtyping`` has no effect when ``control_codes='ignore'``.
359
+
360
+ strip_sequences()
361
+ -----------------
362
+
363
+ Use `strip_sequences()`_ to remove all terminal escape sequences from text.
364
+
365
+ .. code-block:: python
366
+
367
+ >>> from wcwidth import strip_sequences
368
+ >>> strip_sequences('\x1b[31mred\x1b[0m')
369
+ 'red'
370
+
371
+ .. _ambiguous_width:
372
+
373
+ Ambiguous Width
374
+ ---------------
375
+
376
+ Some Unicode characters have "East Asian Ambiguous" (A) width. These characters display as 1 cell by
377
+ default, matching Western terminal contexts, but many CJK (Chinese, Japanese, Korean) environments
378
+ may have a preference for 2 cells. This is often found as boolean option, "Ambiguous width as wide"
379
+ in Terminal Emulator software preferences.
380
+
381
+ The ``ambiguous_width`` parameter is available on all width-measuring functions: `wcwidth()`_,
382
+ `wcswidth()`_, `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
383
+
384
+ By default, wcwidth treats ambiguous characters as narrow (width 1). For CJK environments where your
385
+ terminal is configured to display ambiguous characters as double-width, pass ``ambiguous_width=2``:
386
+
387
+ .. code-block:: python
388
+
389
+ >>> # CIRCLED DIGIT ONE - ambiguous width
390
+ >>> wcwidth.width('\u2460')
391
+ 1
392
+ >>> wcwidth.width('\u2460', ambiguous_width=2)
393
+ 2
394
+
395
+ **Terminal Detection**
396
+
397
+ The most reliable method to detect whether a terminal profile is set for "Ambiguous width as wide"
398
+ mode is to display an ambiguous character surrounded by a pair of Cursor Position Report (CPR)
399
+ queries with a terminal in cooked or raw mode, and to parse the responses for their ``(y, x)``
400
+ locations and measure the difference ``x``.
401
+
402
+ This code should also be careful to check whether it is attached to a terminal and be careful of
403
+ possible timeout, slow network, or non-response when working with "dumb terminals" like a CI build.
404
+
405
+ `jquast/blessed`_ library provides such a helping `Terminal.detect_ambiguous_width()`_ method:
406
+
407
+ .. code-block:: python
408
+
409
+ >>> import blessed, functools
410
+ >>> # Detect terminal ambiguous width as wide (2) or narrow (1)
411
+ >>> ambiguous_width = blessed.Terminal().detect_ambiguous_width()
412
+ >>> # Define a new 'width' function with this argument
413
+ >>> awidth = functools.partial(wcwidth.width, ambiguous_width=ambiguous_width)
414
+ >>> # result depends on attached terminal mode
415
+ >>> awidth('\u2460')
416
+ 1
417
+
418
+ Corrections
419
+ -----------
420
+
421
+ Corrections may be automatically applied depending on the detected or given terminal software name
422
+ beginning with wcwidth release 0.8.0. This allows to correct widths for terminal software that
423
+ differs from the python wcwidth specification_. These corrections are sourced from the
424
+ `jquast/ucs-detect`_ project.
425
+
426
+ The ``term_program`` parameter is available on all width-measuring functions: `wcstwidth()`_,
427
+ `width()`_, `ljust()`_, `rjust()`_, `center()`_, `wrap()`_, and `clip()`_.
428
+
429
+ `wcstwidth()`_ defaults to ``term_program=True``, auto-detecting the terminal from the
430
+ ``TERM_PROGRAM`` or ``TERM`` environment variable. All other functions default to
431
+ ``term_program=False``, disabling corrections. Use ``term_program=True`` for automatic
432
+ detection by environment values of ``TERM`` and ``TERM_PROGRAM``.
433
+
434
+ .. code-block:: python
435
+
436
+ # VTE terminals (Gnome Terminal Et al.) still render trigrams as narrow (1 cell), but their
437
+ # definition was changed to wide in Unicode 16 (September 2024).
438
+ >>> wcwidth.wcswidth('\u2630')
439
+ 2
440
+ >>> wcwidth.wcstwidth('\u2630', term_program='vte')
441
+ 1
442
+
443
+ # account for Alacritty non-support of emoji ZWJ:
444
+ # man + ZWJ + woman + ZWJ + girl + ZWJ + boy
445
+ >>> family = '\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466'
446
+ >>> wcwidth.wcswidth(family)
447
+ 2
448
+ >>> wcwidth.wcstwidth(family, term_program='alacritty')
449
+ 8
450
+
451
+ Only detectable_ terminals are included: those that identify themselves by XTVERSION_, ENQ_, any
452
+ ``TERM_PROGRAM`` or a unique ``TERM`` environment value. For the most accurate correction tables,
453
+ query the terminal's software version via XTVERSION_ (``CSI > q``) using a higher-level interactive
454
+ terminal library like `jquast/blessed`_:
455
+
456
+ .. code-block:: python
457
+
458
+ >>> import blessed, wcwidth
459
+ >>> term = blessed.Terminal()
460
+ >>> sw_ver = term.get_software_version()
461
+ >>> print(sw_ver)
462
+ SoftwareVersion(name='VTE', version='7600')
463
+ >>> wcwidth.width('\u2630', term_program=sw_ver.name)
464
+ 1
465
+
466
+ This is important because ``TERM_PROGRAM`` is not forwarded for remote hosts, like SSH, and many
467
+ terminals may only be identified using XTVERSION_ or ENQ_. Use `list_term_programs()`_ to see all
468
+ recognized names:
469
+
470
+ .. BEGIN_LIST_TERM_PROGRAMS
471
+ .. code-block:: python
472
+
473
+ >>> wcwidth.list_term_programs()
474
+ ('absolutetelnet/ssh', 'alacritty', 'apple_terminal', 'bobcat', 'contour',
475
+ 'extraterm', 'foot', 'ghostty', 'hyper', 'iterm.app', 'iterm2', 'kitty',
476
+ 'konsole', 'mintty', 'mlterm', 'pterm', 'putty', 'rio', 'rxvt',
477
+ 'rxvt-unicode-256color', 'st', 'st-256color', 'tabby', 'terminology',
478
+ 'urxvt', 'vscode', 'vte', 'warp', 'warpterminal', 'wezterm', 'xterm',
479
+ 'xterm-ghostty', 'xterm-kitty', 'xterm.js')
480
+
481
+ .. END_LIST_TERM_PROGRAMS
482
+
483
+ ``term_program=False`` (the default for `width()`_, `ljust()`_, `rjust()`_, `center()`_,
484
+ `wrap()`_, and `clip()`_) disables terminal corrections.
485
+
486
+ For automatic tests and other purposes that require cross-environment consistency, set static values
487
+ or unset ``TERM`` and ``TERM_PROGRAM`` environment values, such as in ``conftest.py`` with pytest:
488
+
489
+ .. code-block:: python
490
+
491
+ @pytest.fixture(autouse=True)
492
+ def _clear_term_program():
493
+ """unset TERM/TERM_PROGRAM before each test."""
494
+ saved_term = os.environ.pop('TERM', None)
495
+ saved_tprog = os.environ.pop('TERM_PROGRAM', None)
496
+ yield
497
+ if saved_term is not None:
498
+ os.environ['TERM'] = saved_term
499
+ if saved_tprog is not None:
500
+ os.environ['TERM_PROGRAM'] = saved_tprog
501
+
502
+ ==================
503
+ More documentation
504
+ ==================
505
+
506
+ Developer documentation, for building and contributing to this project, at
507
+ https://wcwidth.readthedocs.io/en/latest/developing.html
508
+
509
+ Projects using wcwidth, and implementations in other languages, at
510
+ https://wcwidth.readthedocs.io/en/latest/related.html
511
+
512
+ .. _`specification`: https://wcwidth.readthedocs.io/en/latest/specs.html
513
+ .. _`jquast/blessed`: https://github.com/jquast/blessed
514
+ .. _`wcwidth(3)`: https://man7.org/linux/man-pages/man3/wcwidth.3.html
515
+ .. _`wcswidth(3)`: https://man7.org/linux/man-pages/man3/wcswidth.3.html
516
+ .. _`jquast/ucs-detect`: https://github.com/jquast/ucs-detect
517
+ .. _`textwrap.wrap()`: https://docs.python.org/3/library/textwrap.html#textwrap.wrap
518
+ .. _`str.ljust()`: https://docs.python.org/3/library/stdtypes.html#str.ljust
519
+ .. _`str.rjust()`: https://docs.python.org/3/library/stdtypes.html#str.rjust
520
+ .. _`str.center()`: https://docs.python.org/3/library/stdtypes.html#str.center
521
+ .. _`str.expandtabs()`: https://docs.python.org/3/library/stdtypes.html#str.expandtabs
522
+ .. _`General Tabulated Summary`: https://ucs-detect.readthedocs.io/results.html#tabulated-results
523
+ .. _`wcwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcwidth
524
+ .. _`wcswidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcswidth
525
+ .. _`wcstwidth()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wcstwidth
526
+ .. _`width()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.width
527
+ .. _`iter_graphemes()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes
528
+ .. _`iter_graphemes_reverse()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_graphemes_reverse
529
+ .. _`grapheme_boundary_before()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.grapheme_boundary_before
530
+ .. _`ljust()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.ljust
531
+ .. _`rjust()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.rjust
532
+ .. _`center()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.center
533
+ .. _`wrap()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.wrap
534
+ .. _`clip()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.clip
535
+ .. _`strip_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.strip_sequences
536
+ .. _`iter_sequences()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.iter_sequences
537
+ .. _`list_term_programs()`: https://wcwidth.readthedocs.io/en/latest/api.html#wcwidth.list_term_programs
538
+ .. _`Unicode Standard Annex #29`: https://www.unicode.org/reports/tr29/
539
+ .. _`Terminal.detect_ambiguous_width()`: https://blessed.readthedocs.io/en/latest/api/terminal.html#blessed.terminal.Terminal.detect_ambiguous_width
540
+ .. _`Grapheme Clusters and Terminal Emulators`: https://mitchellh.com/writing/grapheme-clusters-in-terminals
541
+ .. _`terminal-unicode-core.tex`: https://github.com/contour-terminal/terminal-unicode-core/blob/master/spec/terminal-unicode-core.tex
542
+ .. _`State of Terminal Emulators in 2025`: https://www.jeffquast.com/post/state-of-terminal-emulation-2025/
543
+ .. _`Perfecting Terminal Character Width Using Correction Tables`: https://www.jeffquast.com/post/perfecting-terminal-character-width-using-correction-tables/
544
+ .. _XTVERSION: https://vtdn.dev/docs/dcs/xtversion/
545
+ .. _ENQ: https://documentation.help/PuTTY/config-answerback.html
546
+ .. _detectable: https://ucs-detect.readthedocs.io/results.html#terminal-identification
547
+ .. _libwcwidth: https://wcwidth.readthedocs.io/en/latest/libwcwidth.html
548
+ .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi
549
+ :alt: Downloads
550
+ :target: https://pypi.org/project/wcwidth/
551
+ .. |codecov| image:: https://codecov.io/gh/jquast/wcwidth/branch/master/graph/badge.svg
552
+ :alt: codecov.io Code Coverage
553
+ :target: https://app.codecov.io/gh/jquast/wcwidth/
554
+ .. |license| image:: https://img.shields.io/pypi/l/wcwidth.svg
555
+ :target: https://pypi.org/project/wcwidth/
556
+ :alt: MIT License
557
+