././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640432451.7299309 cwcwidth-0.1.6/0000755000175100001710000000000000000000000012672 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/CHANGELOG.md0000644000175100001710000000114300000000000014502 0ustar00runnerdocker0.1.6 ----- * Add support for Python 3.10 * Drop support for Python 3.6 0.1.5 ----- * Fix type annotations 0.1.4 ----- * Include tests again. * Include C file again. 0.1.3 ----- * Fix memory leaks in certain error cases. * Modernize build system and rely on setuptool's Cython support. * Add more tests. * Skip some tests if not run in a UTF-8 locale. * Use libc's implementation on Mac OS. 0.1.2 ----- * Also build wheels with cibuildwheel. * Provide type information for mypy. 0.1.1 ----- * If Cython is not available, do not regenerate the C source file during build. 0.1 --- * Initial release. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/LICENSE0000644000175100001710000000205000000000000013674 0ustar00runnerdockerCopyright (c) 2021 Sebastian Ramacher Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/MANIFEST.in0000644000175100001710000000033100000000000014425 0ustar00runnerdockerinclude cwcwidth/_impl.pyx cwcwidth/_impl.c cwcwidth/wcwidth.c cwcwidth/wcwidth_compat.h include cwcwidth/_impl.pyi cwcwidth/py.typed recursive-include tests *.py include CHANGELOG.md exclude .gitignore prune .github ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640432451.7299309 cwcwidth-0.1.6/PKG-INFO0000644000175100001710000000516500000000000013776 0ustar00runnerdockerMetadata-Version: 2.1 Name: cwcwidth Version: 0.1.6 Summary: Python bindings for wc(s)width Home-page: https://github.com/sebastinas/cwcwidth Author: Sebastian Ramacher Author-email: sebastian@ramacher.at License: MIT Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.7 Description-Content-Type: text/markdown License-File: LICENSE # Python bindings for wc(s)width `cwcwidth` provides Python bindings for `wcwidth` and `wcswidth` functions defined in POSIX.1-2001 and POSIX.1-2008 based on [Cython](https://cython.org/). These functions compute the printable length of a unicode character/string on a terminal. The module provides the same functions as [wcwidth](https://pypi.org/project/wcwidth/) and its behavior is compatible. On systems not conforming to POSIX.1-2001 and POSIX.1-2008, Markus Kuhn's [implementation](https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c) is used to provide the functionality. ## Dependencies * `Cython >= 0.28` (optional, only for building). If Cython is not available, the C files are not regenerated from their source. ## Quick installation guide `cwcwidth` can be installed via `pip`: ```sh pip install cwcwidth ``` or by running: ```sh python3 setup.py install ``` ## Usage ```python3 >>> import cwcwidth >>> cwcwidth.wcwidth("a") 1 >>> cwcwidth.wcswidth("コ") 2 >>> cwcwidth.wcswidth("コンニチハ, セカイ!") 19 >>> cwcwidth.wcswidth("コンニチハ, セカイ!", 5) 10 ``` ## Comparison with `wcwidth` ```python3 >>> import wcwidth, cwcwidth, timeit >>> timeit.timeit(lambda: wcwidth.wcswidth("コンニチハ, セカイ!")) 19.14463168097427 >>> timeit.timeit(lambda: cwcwidth.wcswidth("コンニチハ, セカイ!")) 0.16294104099506512 ``` ## License The code is licensed under the MIT license. ## Tidelift ### Security contact information To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coorindate the fix and disclosure. ### Commercial support cwcwidth and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/pypi-cwcwidth?utm_source=pypi-cwcwidth&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/README.md0000644000175100001710000000405600000000000014156 0ustar00runnerdocker# Python bindings for wc(s)width `cwcwidth` provides Python bindings for `wcwidth` and `wcswidth` functions defined in POSIX.1-2001 and POSIX.1-2008 based on [Cython](https://cython.org/). These functions compute the printable length of a unicode character/string on a terminal. The module provides the same functions as [wcwidth](https://pypi.org/project/wcwidth/) and its behavior is compatible. On systems not conforming to POSIX.1-2001 and POSIX.1-2008, Markus Kuhn's [implementation](https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c) is used to provide the functionality. ## Dependencies * `Cython >= 0.28` (optional, only for building). If Cython is not available, the C files are not regenerated from their source. ## Quick installation guide `cwcwidth` can be installed via `pip`: ```sh pip install cwcwidth ``` or by running: ```sh python3 setup.py install ``` ## Usage ```python3 >>> import cwcwidth >>> cwcwidth.wcwidth("a") 1 >>> cwcwidth.wcswidth("コ") 2 >>> cwcwidth.wcswidth("コンニチハ, セカイ!") 19 >>> cwcwidth.wcswidth("コンニチハ, セカイ!", 5) 10 ``` ## Comparison with `wcwidth` ```python3 >>> import wcwidth, cwcwidth, timeit >>> timeit.timeit(lambda: wcwidth.wcswidth("コンニチハ, セカイ!")) 19.14463168097427 >>> timeit.timeit(lambda: cwcwidth.wcswidth("コンニチハ, セカイ!")) 0.16294104099506512 ``` ## License The code is licensed under the MIT license. ## Tidelift ### Security contact information To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coorindate the fix and disclosure. ### Commercial support cwcwidth and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/pypi-cwcwidth?utm_source=pypi-cwcwidth&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640432451.7299309 cwcwidth-0.1.6/cwcwidth/0000755000175100001710000000000000000000000014506 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/cwcwidth/__init__.py0000644000175100001710000000332700000000000016624 0ustar00runnerdocker# Copyright 2021 Sebastian Ramacher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """Bindings for wcwidth(3) and wcswidth(3) This module computes the number of cells a unicode string is expected to occupy on the screen. On systems conforming to POSIX.1-2001 to POSIX.1-2008, this module calls wcwidth(3) and wcswidth(3) provided by C library. On systems where these functions are not available, a compatible implementation is included in the module. This module provides the same interface as the wcwidth module. """ from ._impl import wcwidth, wcswidth __version__ = "0.1.6" __author__ = "Sebastian Ramacher" __license__ = "Expat" __copyright__ = f"(C) 2021 {__author__}" __all__ = ("wcwidth", "wcswidth") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/cwcwidth/_impl.pyi0000644000175100001710000000233500000000000016334 0ustar00runnerdocker# Copyright 2021 Sebastian Ramacher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from typing import Optional def wcwidth(wc: str) -> int: ... def wcswidth(pwcs: str, n: Optional[int] = None) -> int: ... ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/cwcwidth/_impl.pyx0000644000175100001710000000660200000000000016354 0ustar00runnerdocker# Copyright 2021 Sebastian Ramacher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # cython: language_level=3, warn.unused=True from libc.stddef cimport wchar_t, size_t from cpython.mem cimport PyMem_Free cdef extern from "Python.h": wchar_t* PyUnicode_AsWideCharString(object, Py_ssize_t*) except NULL Py_ssize_t PyUnicode_AsWideChar(object, wchar_t*, Py_ssize_t) cdef extern from "wcwidth_compat.h" nogil: int c_wcswidth "wcswidth" (const wchar_t*, size_t) int c_wcwidth "wcwidth" (wchar_t) cdef extern from "" nogil: size_t wcslen(const wchar_t*) cdef int wcswidth_loop(const wchar_t* s, size_t n) nogil: cdef int v cdef int ret = 0 for c in s[:n]: v = c_wcwidth(c) if v == -1: return -1 ret += v return ret def wcswidth(str pwcs not None, n=None): """Return the printable length of a unicode character on a terminal. Note that this function slightly deviates from wcswidth(3) behavior when the string includes null characters. As strings are not null terminated, they are treated as characters of width 0 and processing continues until the end of the string. See wcswidth(3) for more details. """ cdef Py_ssize_t actual_length cdef wchar_t* s = PyUnicode_AsWideCharString(pwcs, &actual_length) cdef size_t length = actual_length cdef size_t null_byte_pos = wcslen(s) cdef size_t converted_n try: if n is not None: converted_n = n if converted_n < length: length = converted_n if actual_length != null_byte_pos: # In this case pwcs contains a null character. libc's wcwidth (and other string # processing functions) will stop when encountering a null character, but in Python the # null character will just be skipped. So in this case we will emulate wcwidth's # behavior and sum up the widths of all characters individually. return wcswidth_loop(s, length) return c_wcswidth(s, length) finally: PyMem_Free(s) def wcwidth(str wc not None): """Return the printable length of a unicode character on a terminal. See wcwidth(3) for more details. """ if len(wc) != 1: raise ValueError("Expected one unicode character") cdef wchar_t c PyUnicode_AsWideChar(wc, &c, 1) return c_wcwidth(c) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/cwcwidth/py.typed0000644000175100001710000000000000000000000016173 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/cwcwidth/wcwidth.c0000644000175100001710000002246200000000000016331 0ustar00runnerdocker/* * This is an implementation of wcwidth() and wcswidth() (defined in * IEEE Std 1002.1-2001) for Unicode. * * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html * * In fixed-width output devices, Latin characters all occupy a single * "cell" position of equal width, whereas ideographic CJK characters * occupy two such cells. Interoperability between terminal-line * applications and (teletype-style) character terminals using the * UTF-8 encoding requires agreement on which character should advance * the cursor by how many cell positions. No established formal * standards exist at present on which Unicode character shall occupy * how many cell positions on character terminals. These routines are * a first attempt of defining such behavior based on simple rules * applied to data provided by the Unicode Consortium. * * For some graphical characters, the Unicode standard explicitly * defines a character-cell width via the definition of the East Asian * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. * In all these cases, there is no ambiguity about which width a * terminal shall use. For characters in the East Asian Ambiguous (A) * class, the width choice depends purely on a preference of backward * compatibility with either historic CJK or Western practice. * Choosing single-width for these characters is easy to justify as * the appropriate long-term solution, as the CJK practice of * displaying these characters as double-width comes from historic * implementation simplicity (8-bit encoded characters were displayed * single-width and 16-bit ones double-width, even for Greek, * Cyrillic, etc.) and not any typographic considerations. * * Much less clear is the choice of width for the Not East Asian * (Neutral) class. Existing practice does not dictate a width for any * of these characters. It would nevertheless make sense * typographically to allocate two character cells to characters such * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be * represented adequately with a single-width glyph. The following * routines at present merely assign a single-cell width to all * neutral characters, in the interest of simplicity. This is not * entirely satisfactory and should be reconsidered before * establishing a formal standard in this area. At the moment, the * decision which Not East Asian (Neutral) characters should be * represented by double-width glyphs cannot yet be answered by * applying a simple rule from the Unicode database content. Setting * up a proper standard for the behavior of UTF-8 character terminals * will require a careful analysis not only of each Unicode character, * but also of each presentation form, something the author of these * routines has avoided to do so far. * * http://www.unicode.org/unicode/reports/tr11/ * * Markus Kuhn -- 2007-05-26 (Unicode 5.0) * * Permission to use, copy, modify, and distribute this software * for any purpose and without fee is hereby granted. The author * disclaims all warranties with regard to this software. * * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c */ #include struct interval { int first; int last; }; /* auxiliary function for binary search in interval table */ static int bisearch(wchar_t ucs, const struct interval *table, int max) { int min = 0; int mid; if (ucs < table[0].first || ucs > table[max].last) return 0; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return 1; } return 0; } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * ISO 8859-1 and WGL4 characters, Unicode control characters, * etc.) have a column width of 1. * * This implementation assumes that wchar_t characters are encoded * in ISO 10646. */ int mk_wcwidth(wchar_t ucs) { /* sorted list of non-overlapping intervals of non-spacing characters */ /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ static const struct interval combining[] = { { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } }; /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ return 1 + (ucs >= 0x1100 && (ucs <= 0x115f || /* Hangul Jamo init. consonants */ ucs == 0x2329 || ucs == 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd))); } int mk_wcswidth(const wchar_t *pwcs, size_t n) { int w, width = 0; for (;*pwcs && n-- > 0; pwcs++) if ((w = mk_wcwidth(*pwcs)) < 0) return -1; else width += w; return width; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/cwcwidth/wcwidth_compat.h0000644000175100001710000000266200000000000017701 0ustar00runnerdocker/** * Copyright 2021 Sebastian Ramacher * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef WCWIDTH_COMPAT_H #define WCWIDTH_COMPAT_H #include #if defined(USE_MK_WCWIDTH) /* Provide declarations for Markus Kuhn's implementation. */ int mk_wcwidth(wchar_t ucs); int mk_wcswidth(const wchar_t *pwcs, size_t n); #define wcwidth mk_wcwidth #define wcswidth mk_wcswidth #endif #endif ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640432451.7299309 cwcwidth-0.1.6/cwcwidth.egg-info/0000755000175100001710000000000000000000000016200 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432451.0 cwcwidth-0.1.6/cwcwidth.egg-info/PKG-INFO0000644000175100001710000000516500000000000017304 0ustar00runnerdockerMetadata-Version: 2.1 Name: cwcwidth Version: 0.1.6 Summary: Python bindings for wc(s)width Home-page: https://github.com/sebastinas/cwcwidth Author: Sebastian Ramacher Author-email: sebastian@ramacher.at License: MIT Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.7 Description-Content-Type: text/markdown License-File: LICENSE # Python bindings for wc(s)width `cwcwidth` provides Python bindings for `wcwidth` and `wcswidth` functions defined in POSIX.1-2001 and POSIX.1-2008 based on [Cython](https://cython.org/). These functions compute the printable length of a unicode character/string on a terminal. The module provides the same functions as [wcwidth](https://pypi.org/project/wcwidth/) and its behavior is compatible. On systems not conforming to POSIX.1-2001 and POSIX.1-2008, Markus Kuhn's [implementation](https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c) is used to provide the functionality. ## Dependencies * `Cython >= 0.28` (optional, only for building). If Cython is not available, the C files are not regenerated from their source. ## Quick installation guide `cwcwidth` can be installed via `pip`: ```sh pip install cwcwidth ``` or by running: ```sh python3 setup.py install ``` ## Usage ```python3 >>> import cwcwidth >>> cwcwidth.wcwidth("a") 1 >>> cwcwidth.wcswidth("コ") 2 >>> cwcwidth.wcswidth("コンニチハ, セカイ!") 19 >>> cwcwidth.wcswidth("コンニチハ, セカイ!", 5) 10 ``` ## Comparison with `wcwidth` ```python3 >>> import wcwidth, cwcwidth, timeit >>> timeit.timeit(lambda: wcwidth.wcswidth("コンニチハ, セカイ!")) 19.14463168097427 >>> timeit.timeit(lambda: cwcwidth.wcswidth("コンニチハ, セカイ!")) 0.16294104099506512 ``` ## License The code is licensed under the MIT license. ## Tidelift ### Security contact information To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coorindate the fix and disclosure. ### Commercial support cwcwidth and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/pypi-cwcwidth?utm_source=pypi-cwcwidth&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432451.0 cwcwidth-0.1.6/cwcwidth.egg-info/SOURCES.txt0000644000175100001710000000061600000000000020067 0ustar00runnerdockerCHANGELOG.md LICENSE MANIFEST.in README.md pyproject.toml setup.cfg setup.py cwcwidth/__init__.py cwcwidth/_impl.pyi cwcwidth/_impl.pyx cwcwidth/py.typed cwcwidth/wcwidth.c cwcwidth/wcwidth_compat.h cwcwidth.egg-info/PKG-INFO cwcwidth.egg-info/SOURCES.txt cwcwidth.egg-info/dependency_links.txt cwcwidth.egg-info/not-zip-safe cwcwidth.egg-info/top_level.txt tests/__init__.py tests/test_cwcwidth.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432451.0 cwcwidth-0.1.6/cwcwidth.egg-info/dependency_links.txt0000644000175100001710000000000100000000000022246 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432451.0 cwcwidth-0.1.6/cwcwidth.egg-info/not-zip-safe0000644000175100001710000000000100000000000020426 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432451.0 cwcwidth-0.1.6/cwcwidth.egg-info/top_level.txt0000644000175100001710000000001100000000000020722 0ustar00runnerdockercwcwidth ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/pyproject.toml0000644000175100001710000000042000000000000015602 0ustar00runnerdocker[build-system] requires = [ "setuptools >= 43", "Cython >= 0.28", "wheel", ] [tool.black] target_version = ["py37"] include = '\.pyi?$' exclude = ''' /( \.git | \.hg | \.eggs | \.mypy_cache | \.tox | venv | _build | build | dist | docs )/ ''' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640432451.7299309 cwcwidth-0.1.6/setup.cfg0000644000175100001710000000116200000000000014513 0ustar00runnerdocker[metadata] name = cwcwidth version = 0.1.6 author = Sebastian Ramacher author_email = sebastian@ramacher.at url = https://github.com/sebastinas/cwcwidth description = Python bindings for wc(s)width long_description = file: README.md long_description_content_type = text/markdown license = MIT license_file = LICENSE classifiers = Development Status :: 4 - Beta Intended Audience :: Developers License :: OSI Approved :: MIT License Programming Language :: Python :: 3 Topic :: Software Development :: Libraries :: Python Modules [options] zip_safe = False python_requires = >=3.7 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/setup.py0000644000175100001710000000113600000000000014405 0ustar00runnerdocker#!/usr/bin/python3 import platform from setuptools import setup, Extension extension_sources = ["cwcwidth/_impl.pyx"] if platform.system() == "Windows": extension_sources.append("cwcwidth/wcwidth.c") define_macros = [ ("USE_MK_WCWIDTH", None), ] else: define_macros = [ ("_XOPEN_SOURCE", "600"), ] ext_modules = [ Extension( "cwcwidth._impl", extension_sources, define_macros=define_macros, ) ] setup( ext_modules=ext_modules, packages=["cwcwidth"], package_data={ "cwcwidth": ["_impl.pyi", "py.typed"], }, ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1640432451.7299309 cwcwidth-0.1.6/tests/0000755000175100001710000000000000000000000014034 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/tests/__init__.py0000644000175100001710000000000000000000000016133 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1640432446.0 cwcwidth-0.1.6/tests/test_cwcwidth.py0000644000175100001710000001015700000000000017265 0ustar00runnerdocker# Copyright 2021 Sebastian Ramacher # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import locale import unittest from cwcwidth import wcwidth, wcswidth supports_utf8 = "UTF-8" in locale.getlocale() class Tests(unittest.TestCase): def _exec_test( self, phrase, individual_expected_lengths, expected_length=None, n=None ): if expected_length is None: expected_length = sum(individual_expected_lengths) self.assertEqual( individual_expected_lengths, tuple(map(wcwidth, phrase if n is None else phrase[:n])), ) self.assertEqual(expected_length, wcswidth(phrase, n)) def test_exceptions(self): """wcwidth raises ValueError for strings of length != 1.""" with self.assertRaises(ValueError): wcwidth("") with self.assertRaises(ValueError): wcwidth("abc") def test_empty_string(self): """Width of an empty string is 0.""" self._exec_test("", tuple()) def test_hello_world(self): """Width of English phrase: Hello World!""" self._exec_test("Hello World!", (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)) @unittest.skipUnless(supports_utf8, "locale does not support UTF-8") def test_hello_jp(self): """Width of Japanese phrase: コンニチハ, セカイ!""" self._exec_test("コンニチハ, セカイ!", (2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 1)) @unittest.skipUnless(supports_utf8, "locale does not support UTF-8") def test_wcswidth_substr(self): """Test wcswidth() optional 2nd parameter.""" self._exec_test("コンニチハ, セカイ!", (2, 2, 2, 2, 2, 1, 1), n=7) def test_null_width_0(self): """NULL (0) reports width 0.""" self._exec_test("abc\x00def", (1, 1, 1, 0, 1, 1, 1)) self._exec_test("abc\x00def", (1, 1, 1, 0, 1, 1, 1), n=7) def test_control_c0_width_negative_1(self): """CSI (Control sequence initiate) reports width -1.""" self._exec_test("\x1b[0m", (-1, 1, 1, 1), expected_length=-1) @unittest.skipUnless(supports_utf8, "locale does not support UTF-8") def test_combining_width_negative_1(self): """Simple test combining reports total width of 4.""" self._exec_test("--\u05bf--", (1, 1, 0, 1, 1)) @unittest.skipUnless(supports_utf8, "locale does not support UTF-8") def test_combining_cafe(self): """Phrase cafe + COMBINING ACUTE ACCENT is café of length 4.""" self._exec_test("cafe\u0301", (1, 1, 1, 1, 0)) @unittest.skipUnless(supports_utf8, "locale does not support UTF-8") def test_combining_enclosing(self): """CYRILLIC CAPITAL LETTER A + COMBINING CYRILLIC HUNDRED THOUSANDS SIGN is А҈ of length 1.""" self._exec_test("\u0410\u0488", (1, 0)) @unittest.skipUnless(supports_utf8, "locale does not support UTF-8") def test_combining_spacing(self): """Balinese kapal (ship) is ᬓᬨᬮ᭄ of length 4.""" self._exec_test("\u1B13\u1B28\u1B2E\u1B44", (1, 1, 1, 1)) def test_carriage_return(self): """Control character reports width -1.""" self._exec_test("\r", (-1,))