jquast / wcwidth

Python library that measures the width of unicode strings rendered to a terminal
Other
393 stars 58 forks source link
wcwidth

|pypi_downloads| |codecov| |license|

============ Introduction

This library is mainly for CLI programs that carefully produce output for Terminals, or make pretend to be an emulator.

Problem Statement: The printable length of most strings are equal to the number of cells they occupy on the screen 1 character : 1 cell. However, there are categories of characters that occupy 2 cells (full-wide), and others that occupy 0 cells (zero-width).

Solution: POSIX.1-2001 and POSIX.1-2008 conforming systems provide wcwidth(3) and wcswidth(3) C functions of which this python module's functions precisely copy. These functions return the number of cells a unicode string is expected to occupy.

Installation

The stable version of this package is maintained on pypi, install using pip::

pip install wcwidth

Example

Problem: given the following phrase (Japanese),

text = u'コンニチハ'

Python incorrectly uses the string length of 5 codepoints rather than the printable length of 10 cells, so that when using the rjust function, the output length is wrong::

>>> print(len('コンニチハ'))
5

>>> print('コンニチハ'.rjust(20, '_'))
_______________コンニチハ

By defining our own "rjust" function that uses wcwidth, we can correct this::

def wc_rjust(text, length, padding=' '): ... from wcwidth import wcswidth ... return padding * max(0, (length - wcswidth(text))) + text ...

Our Solution uses wcswidth to determine the string length correctly::

from wcwidth import wcswidth print(wcswidth('コンニチハ')) 10

print(wcrjust('コンニチハ', 20, '')) __コンニチハ

Choosing a Version

Export an environment variable, UNICODE_VERSION. This should be done by terminal emulators or those developers experimenting with authoring one of their own, from shell::

$ export UNICODE_VERSION=13.0

If unspecified, the latest version is used. If your Terminal Emulator does not export this variable, you can use the jquast/ucs-detect_ utility to automatically detect and export it to your shell.

wcwidth, wcswidth

Use function wcwidth() to determine the length of a single unicode character, and wcswidth() to determine the length of many, a string of unicode characters.

Briefly, return values of function wcwidth() are:

-1 Indeterminate (not printable).

0 Does not advance the cursor, such as NULL or Combining.

2 Characters of category East Asian Wide (W) or East Asian Full-width (F) which are displayed using two terminal cells.

1 All others.

Function wcswidth() simply returns the sum of all values for each character along a string, or -1 when it occurs anywhere along a string.

Full API Documentation at https://wcwidth.readthedocs.org

========== Developing

Install wcwidth in editable mode::

pip install -e .

Execute unit tests using tox_::

tox -e py27,py35,py36,py37,py38,py39,py310,py311,py312

Updating Unicode Version

Regenerate python code tables from latest Unicode Specification data files::

tox -e update

The script is located at bin/update-tables.py, requires Python 3.9 or later. It is recommended but not necessary to run this script with the newest Python, because the newest Python has the latest unicodedata for generating comments.

Building Documentation

This project is using sphinx_ 4.5 to build documentation::

tox -e sphinx

The output will be in docs/_build/html/.

Updating Requirements

This project is using pip-tools_ to manage requirements.

To upgrade requirements for updating unicode version, run::

tox -e update_requirements_update

To upgrade requirements for testing, run::

tox -e update_requirements37,update_requirements39

To upgrade requirements for building documentation, run::

tox -e update_requirements_docs

Utilities

Supplementary tools for browsing and testing terminals for wide unicode characters are found in the bin/_ of this project's source code. Just ensure to first pip install -r requirements-develop.txt from this projects main folder. For example, an interactive browser for testing::

python ./bin/wcwidth-browser.py

==== Uses

This library is used in:

=============== Other Languages

======= History

0.2.13 2024-01-06

0.2.12 2023-11-21

0.2.11 2023-11-20

0.2.10 2023-11-13

0.2.9 2023-10-30

0.2.8 2023-09-30

0.2.7 2023-09-28

0.2.6 2023-01-14

0.2.1 .. 0.2.5 2020-06-23

0.2.0 2020-06-01

0.1.9 2020-03-22

0.1.8 2020-01-01

0.1.7 2016-07-01

0.1.6 2016-01-08 Production/Stable

0.1.5 2015-09-13 Alpha

0.1.4 2014-11-20 Pre-Alpha

0.1.3 2014-10-29 Pre-Alpha

0.1.2 2014-10-28 Pre-Alpha

0.1.1 2014-05-14 Pre-Alpha

This code was originally derived directly from C code of the same name, whose latest version is available at https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c::

.. _Specification_from_pypi: https://wcwidth.readthedocs.io/en/latest/specs.html .. tox: https://tox.wiki/en/latest/ .. prospector: https://github.com/landscapeio/prospector .. _combining: https://en.wikipedia.org/wiki/Combining_character .. bin/: https://github.com/jquast/wcwidth/tree/master/bin .. bin/wcwidth-browser.py: https://github.com/jquast/wcwidth/blob/master/bin/wcwidth-browser.py .. Thomas Ballinger: https://github.com/thomasballinger .. Leta Montopoli: https://github.com/lmontopo .. Philip Craig: https://github.com/philipc .. PR #3: https://github.com/jquast/wcwidth/pull/3 .. PR #4: https://github.com/jquast/wcwidth/pull/4 .. PR #5: https://github.com/jquast/wcwidth/pull/5 .. PR #11: https://github.com/jquast/wcwidth/pull/11 .. PR #18: https://github.com/jquast/wcwidth/pull/18 .. PR #30: https://github.com/jquast/wcwidth/pull/30 .. PR #35: https://github.com/jquast/wcwidth/pull/35 .. PR #82: https://github.com/jquast/wcwidth/pull/82 .. PR #91: https://github.com/jquast/wcwidth/pull/91 .. PR #97: https://github.com/jquast/wcwidth/pull/97 .. PR #98: https://github.com/jquast/wcwidth/pull/98 .. PR #100: https://github.com/jquast/wcwidth/pull/100 .. Issue #101: https://github.com/jquast/wcwidth/issues/101 .. jquast/blessed: https://github.com/jquast/blessed .. selectel/pyte: https://github.com/selectel/pyte .. thomasballinger/curtsies: https://github.com/thomasballinger/curtsies .. dbcli/pgcli: https://github.com/dbcli/pgcli .. prompt-toolkit/python-prompt-toolkit: https://github.com/prompt-toolkit/python-prompt-toolkit .. timoxley/wcwidth: https://github.com/timoxley/wcwidth .. wcwidth(3): https://man7.org/linux/man-pages/man3/wcwidth.3.html .. wcswidth(3): https://man7.org/linux/man-pages/man3/wcswidth.3.html .. astanin/python-tabulate: https://github.com/astanin/python-tabulate .. janlelis/unicode-display_width: https://github.com/janlelis/unicode-display_width .. rspeer/python-ftfy: https://github.com/rspeer/python-ftfy .. alecrabbit/php-wcwidth: https://github.com/alecrabbit/php-wcwidth .. Text::CharWidth: https://metacpan.org/pod/Text::CharWidth .. bluebear94/Terminal-WCWidth: https://github.com/bluebear94/Terminal-WCWidth .. mattn/go-runewidth: https://github.com/mattn/go-runewidth .. grepsuzette/wcwidth: https://github.com/grepsuzette/wcwidth .. jquast/ucs-detect: https://github.com/jquast/ucs-detect .. Avram Lubkin: https://github.com/avylove .. nbedos/termtosvg: https://github.com/nbedos/termtosvg .. peterbrittain/asciimatics: https://github.com/peterbrittain/asciimatics .. aperezdc/lua-wcwidth: https://github.com/aperezdc/lua-wcwidth .. joachimschmidt557/zig-wcwidth: https://github.com/joachimschmidt557/zig-wcwidth .. fumiyas/wcwidth-cjk: https://github.com/fumiyas/wcwidth-cjk .. joshuarubin/wcwidth9: https://github.com/joshuarubin/wcwidth9 .. python-cmd2/cmd2: https://github.com/python-cmd2/cmd2 .. stratis-storage/stratis-cli: https://github.com/stratis-storage/stratis-cli .. ihabunek/toot: https://github.com/ihabunek/toot .. saulpw/visidata: https://github.com/saulpw/visidata .. pip-tools: https://pip-tools.readthedocs.io/ .. sphinx: https://www.sphinx-doc.org/ .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/wcwidth.svg?logo=pypi :alt: Downloads :target: https://pypi.org/project/wcwidth/ .. |codecov| image:: https://codecov.io/gh/jquast/wcwidth/branch/master/graph/badge.svg :alt: codecov.io Code Coverage :target: https://app.codecov.io/gh/jquast/wcwidth/ .. |license| image:: https://img.shields.io/pypi/l/wcwidth.svg :target: https://pypi.org/project/wcwidth/ :alt: MIT License