Skip to content

back to Claude Sonnet 3.5 - Base summary

Claude Sonnet 3.5 - Base: wcwidth

Pytest Summary for test tests

status count
passed 6
failed 32
skipped 1
total 39
collected 39

Failed pytests:

test_core.py::test_empty_string

test_core.py::test_empty_string
def test_empty_string():
        """
        Test empty string is OK.

        https://github.com/jquast/wcwidth/issues/24
        """
        phrase = ""
        expect_length_each = 0
        expect_length_phrase = 0

        # exercise,
>       length_each = wcwidth.wcwidth(phrase)

tests/test_core.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

wc = '', unicode_version = 'auto'

    @lru_cache(maxsize=1000)
    def wcwidth(wc, unicode_version='auto'):
        """
        Given one Unicode character, return its printable length on a terminal.

        :param str wc: A single Unicode character.
        :param str unicode_version: A Unicode version number, such as
            ``'6.0.0'``. A list of version levels suported by wcwidth
            is returned by :func:`list_versions`.

            Any version string may be specified without error -- the nearest
            matching version is selected.  When ``latest`` (default), the
            highest Unicode version level is used.
        :return: The width, in cells, necessary to display the character of
            Unicode string character, ``wc``.  Returns 0 if the ``wc`` argument has
            no printable effect on a terminal (such as NUL '\\0'), -1 if ``wc`` is
            not printable, or has an indeterminate effect on the terminal, such as
            a control character.  Otherwise, the number of column positions the
            character occupies on a graphic terminal (1 or 2) is returned.
        :rtype: int

        See :ref:`Specification` for details of cell measurement.
        """
>       ucs = ord(wc)
E       TypeError: ord() expected a character, but string of length 0 found

wcwidth/wcwidth.py:129: TypeError

test_core.py::test_hello_jp

test_core.py::test_hello_jp
def test_hello_jp():
        u"""
        Width of Japanese phrase: コンニチハ, セカイ!

        Given a phrase of 5 and 3 Katakana ideographs, joined with
        3 English-ASCII punctuation characters, totaling 11, this
        phrase consumes 19 cells of a terminal emulator.
        """
        # given,
        phrase = u'コンニチハ, セカイ!'
        expect_length_each = (2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 1)
        expect_length_phrase = sum(expect_length_each)

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:86: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 12467
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_wcswidth_substr

test_core.py::test_wcswidth_substr
def test_wcswidth_substr():
        """
        Test wcswidth() optional 2nd parameter, ``n``.

        ``n`` determines at which position of the string
        to stop counting length.
        """
        # given,
        phrase = u'コンニチハ, セカイ!'
        end = 7
        expect_length_each = (2, 2, 2, 2, 2, 1, 1,)
        expect_length_phrase = sum(expect_length_each)

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))[:end]

tests/test_core.py:108: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 12467
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_null_width_0

test_core.py::test_null_width_0
def test_null_width_0():
        """NULL (0) reports width 0."""
        # given,
        phrase = u'abc\x00def'
        expect_length_each = (1, 1, 1, 0, 1, 1, 1)
        expect_length_phrase = sum(expect_length_each)

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:124: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 97
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_control_c0_width_negative_1

test_core.py::test_control_c0_width_negative_1
def test_control_c0_width_negative_1():
        """How the API reacts to CSI (Control sequence initiate).

        An example of bad fortune, this terminal sequence is a width of 0
        on all terminals, but wcwidth doesn't parse Control-Sequence-Inducer
        (CSI) sequences.

        Also the "legacy" posix functions wcwidth and wcswidth return -1 for
        any string containing the C1 control character \x1b (ESC).
        """
        # given,
        phrase = u'\x1b[0m'
        expect_length_each = (-1, 1, 1, 1)
        expect_length_phrase = -1

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:148: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 91
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_combining_width

test_core.py::test_combining_width
def test_combining_width():
        """Simple test combining reports total width of 4."""
        # given,
        phrase = u'--\u05bf--'
        expect_length_each = (1, 1, 0, 1, 1)
        expect_length_phrase = 4

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 45
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_combining_cafe

test_core.py::test_combining_cafe
def test_combining_cafe():
        u"""Phrase cafe + COMBINING ACUTE ACCENT is café of length 4."""
        phrase = u"cafe\u0301"
        expect_length_each = (1, 1, 1, 1, 0)
        expect_length_phrase = 4

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:179: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 99
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_combining_enclosing

test_core.py::test_combining_enclosing
def test_combining_enclosing():
        u"""CYRILLIC CAPITAL LETTER A + COMBINING CYRILLIC HUNDRED THOUSANDS SIGN is of length 1."""
        phrase = u"\u0410\u0488"
        expect_length_each = (1, 0)
        expect_length_phrase = 1

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:194: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 1040
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_balinese_script

test_core.py::test_balinese_script
def test_balinese_script():
        u"""
        Balinese kapal (ship) is length 3.

        This may be an example that is not yet correctly rendered by any terminal so
        far, like devanagari.
        """
        phrase = (u"\u1B13"    # Category 'Lo', EAW 'N' -- BALINESE LETTER KA
                  u"\u1B28"    # Category 'Lo', EAW 'N' -- BALINESE LETTER PA KAPAL
                  u"\u1B2E"    # Category 'Lo', EAW 'N' -- BALINESE LETTER LA
                  u"\u1B44")   # Category 'Mc', EAW 'N' -- BALINESE ADEG ADEG
        expect_length_each = (1, 1, 1, 0)
        expect_length_phrase = 3

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:217: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 6931
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_kr_jamo

test_core.py::test_kr_jamo
def test_kr_jamo():
        """
        Test basic combining of HANGUL CHOSEONG and JUNGSEONG

        Example and from Raymond Chen's blog post,
        https://devblogs.microsoft.com/oldnewthing/20201009-00/?p=104351
        """
        # This is an example where both characters are "wide" when displayed alone.
        #
        # But JUNGSEONG (vowel) is designed for combination with a CHOSEONG (consonant).
        #
        # This wcwidth library understands their width only when combination,
        # and not by independent display, like other zero-width characters that may
        # only combine with an appropriate preceding character.
        phrase = (
            u"\u1100"  # ᄀ HANGUL CHOSEONG KIYEOK (consonant)
            u"\u1161"  # ᅡ HANGUL JUNGSEONG A (vowel)
        )
        expect_length_each = (2, 0)
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:247: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 4352
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_kr_jamo_filler

test_core.py::test_kr_jamo_filler
def test_kr_jamo_filler():
        u"""
        Jamo filler is 0 width.

        Example from https://www.unicode.org/L2/L2006/06310-hangul-decompose9.pdf
        """
        phrase = (
            u"\u1100"  # HANGUL CHOSEONG KIYEOK (consonant)
            u"\u1160"  # HANGUL JUNGSEONG FILLER (vowel)
        )
        expect_length_each = (2, 0)
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:269: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 4352
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_devanagari_script

test_core.py::test_devanagari_script
def test_devanagari_script():
        """
        Attempt to test the measurement width of Devanagari script.

        I believe this 'phrase' should be length 3.

        This is a difficult problem, and this library does not yet get it right,
        because we interpret the unicode data files programmatically, but they do
        not correctly describe how their terminal width is measured.

        There are very few Terminals that do!

        As of 2023,

        - iTerm2: correct length but individual characters are out of order and
                  horizaontally misplaced as to be unreadable in its language when
                  using 'Noto Sans' font.
        - mlterm: mixed results, it offers several options in the configuration
                  dialog, "Xft", "Cario", and "Variable Column Width" have some
                  effect, but with neither 'Noto Sans' or 'unifont', it is not
                  recognizable as the Devanagari script it is meant to display.

        Previous testing with Devanagari documented at address https://benizi.com/vim/devanagari/

        See also, https://askubuntu.com/questions/8437/is-there-a-good-mono-spaced-font-for-devanagari-script-in-the-terminal
        """
        # This test adapted from https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
        # please note that document correctly points out that the final width cannot be determined
        # as a sum of each individual width, as this library currently performs with exception of
        # ZWJ, but I think it incorrectly gestures what a stateless call to wcwidth.wcwidth of
        # each codepoint *should* return.
        phrase = (u"\u0915"    # Akhand, Category 'Lo', East Asian Width property 'N' -- DEVANAGARI LETTER KA
                  u"\u094D"    # Joiner, Category 'Mn', East Asian Width property 'N' -- DEVANAGARI SIGN VIRAMA
                  u"\u0937"    # Fused, Category 'Lo', East Asian Width property 'N' -- DEVANAGARI LETTER SSA
                  u"\u093F")   # MatraL, Category 'Mc', East Asian Width property 'N' -- DEVANAGARI VOWEL SIGN I
        # 23107-terminal-suppt.pdf suggests wcwidth.wcwidth should return (2, 0, 0, 1)
        expect_length_each = (1, 0, 1, 0)
        # I believe the final width *should* be 3.
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 2325
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_tamil_script

test_core.py::test_tamil_script
def test_tamil_script():
        # This test adapted from https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
        phrase = (u"\u0b95"    # Akhand, Category 'Lo', East Asian Width property 'N' -- TAMIL LETTER KA
                  u"\u0bcd"    # Joiner, Category 'Mn', East Asian Width property 'N' -- TAMIL SIGN VIRAMA
                  u"\u0bb7"    # Fused, Category 'Lo', East Asian Width property 'N' -- TAMIL LETTER SSA
                  u"\u0bcc")   # MatraLR, Category 'Mc', East Asian Width property 'N' -- TAMIL VOWEL SIGN AU
        # 23107-terminal-suppt.pdf suggests wcwidth.wcwidth should return (3, 0, 0, 4)
        expect_length_each = (1, 0, 1, 0)

        # I believe the final width should be about 5 or 6.
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:339: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 2965
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_kannada_script

test_core.py::test_kannada_script
def test_kannada_script():
        # This test adapted from https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
        # |ರ್ಝೈ|
        # |123|
        phrase = (u"\u0cb0"    # Repha, Category 'Lo', East Asian Width property 'N' -- KANNADA LETTER RA
                  u"\u0ccd"    # Joiner, Category 'Mn', East Asian Width property 'N' -- KANNADA SIGN VIRAMA
                  u"\u0c9d"    # Base, Category 'Lo', East Asian Width property 'N' -- KANNADA LETTER JHA
                  u"\u0cc8")   # MatraUR, Category 'Mc', East Asian Width property 'N' -- KANNADA VOWEL SIGN AI
        # 23107-terminal-suppt.pdf suggests should be (2, 0, 3, 1)
        expect_length_each = (1, 0, 1, 0)
        # I believe the correct final width *should* be 3 or 4.
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:361: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 3248
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_kannada_script_2

test_core.py::test_kannada_script_2
def test_kannada_script_2():
        # This test adapted from https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
        # |ರ಼್ಚ|
        # |12|
        phrase = (u"\u0cb0"    # Base, Category 'Lo', East Asian Width property 'N' -- KANNADA LETTER RA
                  u"\u0cbc"    # Nukta, Category 'Mn', East Asian Width property 'N' -- KANNADA SIGN NUKTA
                  u"\u0ccd"    # Joiner, Category 'Lo', East Asian Width property 'N' -- KANNADA SIGN VIRAMA
                  u"\u0c9a")   # Subjoin, Category 'Mc', East Asian Width property 'N' -- KANNADA LETTER CA
        # 23107-terminal-suppt.pdf suggests wcwidth.wcwidth should return (2, 0, 0, 1)
        expect_length_each = (1, 0, 0, 1)
        # I believe the final width is correct, but maybe for the wrong reasons!
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_core.py:383: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 3248
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_core.py::test_zero_wide_conflict

test_core.py::test_zero_wide_conflict
def test_zero_wide_conflict():
        # Test characters considered both "wide" and "zero" width
        # -  (0x03000, 0x0303e,),  # Ideographic Space       ..Ideographic Variation In
        # +  (0x03000, 0x03029,),  # Ideographic Space       ..Hangzhou Numeral Nine
>       assert wcwidth.wcwidth(unichr(0x03029), unicode_version='4.1.0') == 2

tests/test_core.py:395: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 12329
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_emojis.py::test_unfinished_zwj_sequence

test_emojis.py::test_unfinished_zwj_sequence
@pytest.mark.skipif(NARROW_ONLY, reason="Test cannot verify on python 'narrow' builds")
    def test_unfinished_zwj_sequence():
        u"""
        Ensure index-out-of-bounds does not occur for zero-width joiner without any following character
        """
        phrase = (u"\U0001f469"   # Base, Category So, East Asian Width property 'W' -- WOMAN
                  u"\U0001f3fb"   # Modifier, Category Sk, East Asian Width property 'W' -- EMOJI MODIFIER FITZPATRICK TYPE-1-2
                  u"\u200d")      # Joiner, Category Cf, East Asian Width property 'N'  -- ZERO WIDTH JOINER
        expect_length_each = (2, 0, 0)
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_emojis.py:67: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 128105
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError
test_emojis.py::test_non_recommended_zwj_sequence
@pytest.mark.skipif(NARROW_ONLY, reason="Test cannot verify on python 'narrow' builds")
    def test_non_recommended_zwj_sequence():
        """
        Verify ZWJ is measured as though successful with characters that cannot be joined, wcwidth does not verify
        """
        phrase = (u"\U0001f469"   # Base, Category So, East Asian Width property 'W' -- WOMAN
                  u"\U0001f3fb"   # Modifier, Category Sk, East Asian Width property 'W' -- EMOJI MODIFIER FITZPATRICK TYPE-1-2
                  u"\u200d")      # Joiner, Category Cf, East Asian Width property 'N'  -- ZERO WIDTH JOINER
        expect_length_each = (2, 0, 0)
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_emojis.py:87: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 128105
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_emojis.py::test_another_emoji_zwj_sequence

test_emojis.py::test_another_emoji_zwj_sequence
@pytest.mark.skipif(NARROW_ONLY, reason="Test cannot verify on python 'narrow' builds")
    def test_another_emoji_zwj_sequence():
        phrase = (
            u"\u26F9"        # PERSON WITH BALL
            u"\U0001F3FB"    # EMOJI MODIFIER FITZPATRICK TYPE-1-2
            u"\u200D"        # ZERO WIDTH JOINER
            u"\u2640"        # FEMALE SIGN
            u"\uFE0F")       # VARIATION SELECTOR-16
        expect_length_each = (1, 0, 0, 1, 0)
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_emojis.py:107: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 9977
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_emojis.py::test_longer_emoji_zwj_sequence

test_emojis.py::test_longer_emoji_zwj_sequence
@pytest.mark.skipif(NARROW_ONLY, reason="Test cannot verify on python 'narrow' builds")
    def test_longer_emoji_zwj_sequence():
        """
        A much longer emoji ZWJ sequence of 10 total codepoints is just 2 cells!

        Also test the same sequence in duplicate, verifying multiple VS-16 sequences
        in a single function call.
        """
        # 'Category Code', 'East Asian Width property' -- 'description'
        phrase = (u"\U0001F9D1"   # 'So', 'W' -- ADULT
                  u"\U0001F3FB"   # 'Sk', 'W' -- EMOJI MODIFIER FITZPATRICK TYPE-1-2
                  u"\u200d"       # 'Cf', 'N' -- ZERO WIDTH JOINER
                  u"\u2764"       # 'So', 'N' -- HEAVY BLACK HEART
                  u"\uFE0F"       # 'Mn', 'A' -- VARIATION SELECTOR-16
                  u"\u200d"       # 'Cf', 'N' -- ZERO WIDTH JOINER
                  u"\U0001F48B"   # 'So', 'W' -- KISS MARK
                  u"\u200d"       # 'Cf', 'N' -- ZERO WIDTH JOINER
                  u"\U0001F9D1"   # 'So', 'W' -- ADULT
                  u"\U0001F3FD"   # 'Sk', 'W' -- EMOJI MODIFIER FITZPATRICK TYPE-4
        ) * 2
        # This test adapted from https://www.unicode.org/L2/L2023/23107-terminal-suppt.pdf
        expect_length_each = (2, 0, 0, 1, 0, 0, 2, 0, 2, 0) * 2
        expect_length_phrase = 4

        # exercise,
>       length_each = tuple(map(wcwidth.wcwidth, phrase))

tests/test_emojis.py:140: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 129489
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError
test_emojis.py::test_recommended_emoji_zwj_sequences
@pytest.mark.skipif(NARROW_ONLY, reason="Some sequences in text file are not compatible with 'narrow' builds")
    def test_recommended_emoji_zwj_sequences():
        """
        Test wcswidth of all of the unicode.org-published emoji-zwj-sequences.txt
        """
        # given,
        lines, sequences = read_sequences_from_file('emoji-zwj-sequences.txt')

        errors = []
        # Exercise, track by zipping with original text file line, a debugging aide
        num = 0
        for sequence, line in zip(sequences, lines):
            num += 1
>           measured_width = wcwidth.wcswidth(sequence)

tests/test_emojis.py:171: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:177: in wcswidth
    char_width = wcwidth(char, unicode_version)
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 128104
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError
test_emojis.py::test_recommended_variation_16_sequences
def test_recommended_variation_16_sequences():
        """
        Test wcswidth of all of the unicode.org-published emoji-variation-sequences.txt
        """
        # given,
        lines, sequences = read_sequences_from_file('emoji-variation-sequences.txt')

        errors = []
        num = 0
        for sequence, line in zip(sequences, lines):
            num += 1
            if '\ufe0f' not in sequence:
                # filter for only \uFE0F (VS-16)
                continue
>           measured_width = wcwidth.wcswidth(sequence)

tests/test_emojis.py:199: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:177: in wcswidth
    char_width = wcwidth(char, unicode_version)
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 35
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_emojis.py::test_unicode_9_vs16

test_emojis.py::test_unicode_9_vs16
def test_unicode_9_vs16():
        """Verify effect of VS-16 on unicode_version 9.0 and later"""
        phrase = (u"\u2640"        # FEMALE SIGN
                  u"\uFE0F")       # VARIATION SELECTOR-16

        expect_length_each = (1, 0)
        expect_length_phrase = 2

        # exercise,
>       length_each = tuple(wcwidth.wcwidth(w_char, unicode_version='9.0') for w_char in phrase)

tests/test_emojis.py:222: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_emojis.py:222: in 
    length_each = tuple(wcwidth.wcwidth(w_char, unicode_version='9.0') for w_char in phrase)
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 9792
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_emojis.py::test_unicode_8_vs16

test_emojis.py::test_unicode_8_vs16
def test_unicode_8_vs16():
        """Verify that VS-16 has no effect on unicode_version 8.0 and earler"""
        phrase = (u"\u2640"        # FEMALE SIGN
                  u"\uFE0F")       # VARIATION SELECTOR-16

        expect_length_each = (1, 0)
        expect_length_phrase = 1

        # exercise,
>       length_each = tuple(wcwidth.wcwidth(w_char, unicode_version='8.0') for w_char in phrase)

tests/test_emojis.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_emojis.py:238: in 
    length_each = tuple(wcwidth.wcwidth(w_char, unicode_version='8.0') for w_char in phrase)
wcwidth/wcwidth.py:136: in wcwidth
    if _bisearch(ucs, ZERO_WIDTH):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ucs = 9792
table = {'10.0.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), '11.0.0': ((0, 0), (173, 1...9), (1471, 1471), ...), '12.1.0': ((0, 0), (173, 173), (768, 879), (1155, 1161), (1425, 1469), (1471, 1471), ...), ...}

    def _bisearch(ucs, table):
        """
        Auxiliary function for binary search in interval table.

        :arg int ucs: Ordinal value of unicode character.
        :arg list table: List of starting and ending ranges of ordinal values,
            in form of ``[(start, end), ...]``.
        :rtype: int
        :returns: 1 if ordinal value ucs is found within lookup table, else 0.
        """
        lbound = 0
        ubound = len(table) - 1

>       if ucs < table[0][0] or ucs > table[-1][1]:
E       KeyError: 0

wcwidth/wcwidth.py:91: KeyError

test_ucslevel.py::test_nearest_505_str

test_ucslevel.py::test_nearest_505_str
def test_nearest_505_str():
        """wcwidth._wcmatch_version('5.0.5') returns nearest '5.0.0'. (str)"""
        # given
        given, expected = '5.0.5', '5.0.0'

        # exercise
>       result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:55: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

given_version = '5.0.5'

    @lru_cache(maxsize=8)
    def _wcmatch_version(given_version):
        """
        Return nearest matching supported Unicode version level.

        If an exact match is not determined, the nearest lowest version level is
        returned after a warning is emitted.  For example, given supported levels
        ``4.1.0`` and ``5.0.0``, and a version string of ``4.9.9``, then ``4.1.0``
        is selected and returned:

        >>> _wcmatch_version('4.9.9')
        '4.1.0'
        >>> _wcmatch_version('8.0')
        '8.0.0'
        >>> _wcmatch_version('1')
        '4.1.0'

        :param str given_version: given version for compare, may be ``auto``
            (default), to select Unicode Version from Environment Variable,
            ``UNICODE_VERSION``. If the environment variable is not set, then the
            latest is used.
        :rtype: str
        :returns: unicode string, or non-unicode ``str`` type for python 2
            when given ``version`` is also type ``str``.
        """
        if given_version == 'auto':
            given_version = os.environ.get('UNICODE_VERSION', 'latest')

        if given_version == 'latest':
            return list_versions()[-1]

        supported_versions = list_versions()
        given_value = _wcversion_value(given_version)

        for version in reversed(supported_versions):
            if _wcversion_value(version) <= given_value:
                if version != given_version:
>                   warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
E                   UserWarning: Unicode version '5.0.5' not found, using '5.0.0'

wcwidth/wcwidth.py:234: UserWarning

test_ucslevel.py::test_nearest_505_unicode

test_ucslevel.py::test_nearest_505_unicode
def test_nearest_505_unicode():
        """wcwidth._wcmatch_version(u'5.0.5') returns nearest u'5.0.0'. (unicode)"""
        # given
        given, expected = u'5.0.5', u'5.0.0'

        # exercise
>       result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:67: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

given_version = '5.0.5'

    @lru_cache(maxsize=8)
    def _wcmatch_version(given_version):
        """
        Return nearest matching supported Unicode version level.

        If an exact match is not determined, the nearest lowest version level is
        returned after a warning is emitted.  For example, given supported levels
        ``4.1.0`` and ``5.0.0``, and a version string of ``4.9.9``, then ``4.1.0``
        is selected and returned:

        >>> _wcmatch_version('4.9.9')
        '4.1.0'
        >>> _wcmatch_version('8.0')
        '8.0.0'
        >>> _wcmatch_version('1')
        '4.1.0'

        :param str given_version: given version for compare, may be ``auto``
            (default), to select Unicode Version from Environment Variable,
            ``UNICODE_VERSION``. If the environment variable is not set, then the
            latest is used.
        :rtype: str
        :returns: unicode string, or non-unicode ``str`` type for python 2
            when given ``version`` is also type ``str``.
        """
        if given_version == 'auto':
            given_version = os.environ.get('UNICODE_VERSION', 'latest')

        if given_version == 'latest':
            return list_versions()[-1]

        supported_versions = list_versions()
        given_value = _wcversion_value(given_version)

        for version in reversed(supported_versions):
            if _wcversion_value(version) <= given_value:
                if version != given_version:
>                   warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
E                   UserWarning: Unicode version '5.0.5' not found, using '5.0.0'

wcwidth/wcwidth.py:234: UserWarning

test_ucslevel.py::test_nearest_800_str

test_ucslevel.py::test_nearest_800_str
def test_nearest_800_str():
        """wcwidth._wcmatch_version('8') returns nearest '8.0.0'."""
        # given
        given, expected = '8', '8.0.0'

        # exercise
>       result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:111: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

given_version = '8'

    @lru_cache(maxsize=8)
    def _wcmatch_version(given_version):
        """
        Return nearest matching supported Unicode version level.

        If an exact match is not determined, the nearest lowest version level is
        returned after a warning is emitted.  For example, given supported levels
        ``4.1.0`` and ``5.0.0``, and a version string of ``4.9.9``, then ``4.1.0``
        is selected and returned:

        >>> _wcmatch_version('4.9.9')
        '4.1.0'
        >>> _wcmatch_version('8.0')
        '8.0.0'
        >>> _wcmatch_version('1')
        '4.1.0'

        :param str given_version: given version for compare, may be ``auto``
            (default), to select Unicode Version from Environment Variable,
            ``UNICODE_VERSION``. If the environment variable is not set, then the
            latest is used.
        :rtype: str
        :returns: unicode string, or non-unicode ``str`` type for python 2
            when given ``version`` is also type ``str``.
        """
        if given_version == 'auto':
            given_version = os.environ.get('UNICODE_VERSION', 'latest')

        if given_version == 'latest':
            return list_versions()[-1]

        supported_versions = list_versions()
        given_value = _wcversion_value(given_version)

        for version in reversed(supported_versions):
            if _wcversion_value(version) <= given_value:
                if version != given_version:
>                   warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
E                   UserWarning: Unicode version '8' not found, using '7.0.0'

wcwidth/wcwidth.py:234: UserWarning

test_ucslevel.py::test_nearest_800_unicode

test_ucslevel.py::test_nearest_800_unicode
def test_nearest_800_unicode():
        """wcwidth._wcmatch_version(u'8') returns nearest u'8.0.0'."""
        # given
        given, expected = u'8', u'8.0.0'

        # exercise
>       result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:123: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

given_version = '8'

    @lru_cache(maxsize=8)
    def _wcmatch_version(given_version):
        """
        Return nearest matching supported Unicode version level.

        If an exact match is not determined, the nearest lowest version level is
        returned after a warning is emitted.  For example, given supported levels
        ``4.1.0`` and ``5.0.0``, and a version string of ``4.9.9``, then ``4.1.0``
        is selected and returned:

        >>> _wcmatch_version('4.9.9')
        '4.1.0'
        >>> _wcmatch_version('8.0')
        '8.0.0'
        >>> _wcmatch_version('1')
        '4.1.0'

        :param str given_version: given version for compare, may be ``auto``
            (default), to select Unicode Version from Environment Variable,
            ``UNICODE_VERSION``. If the environment variable is not set, then the
            latest is used.
        :rtype: str
        :returns: unicode string, or non-unicode ``str`` type for python 2
            when given ``version`` is also type ``str``.
        """
        if given_version == 'auto':
            given_version = os.environ.get('UNICODE_VERSION', 'latest')

        if given_version == 'latest':
            return list_versions()[-1]

        supported_versions = list_versions()
        given_value = _wcversion_value(given_version)

        for version in reversed(supported_versions):
            if _wcversion_value(version) <= given_value:
                if version != given_version:
>                   warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
E                   UserWarning: Unicode version '8' not found, using '7.0.0'

wcwidth/wcwidth.py:234: UserWarning

test_ucslevel.py::test_nearest_999_str

test_ucslevel.py::test_nearest_999_str
def test_nearest_999_str():
        """wcwidth._wcmatch_version('999.0') returns nearest (latest)."""
        # given
        given, expected = '999.0', wcwidth.list_versions()[-1]

        # exercise
>       result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:135: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

given_version = '999.0'

    @lru_cache(maxsize=8)
    def _wcmatch_version(given_version):
        """
        Return nearest matching supported Unicode version level.

        If an exact match is not determined, the nearest lowest version level is
        returned after a warning is emitted.  For example, given supported levels
        ``4.1.0`` and ``5.0.0``, and a version string of ``4.9.9``, then ``4.1.0``
        is selected and returned:

        >>> _wcmatch_version('4.9.9')
        '4.1.0'
        >>> _wcmatch_version('8.0')
        '8.0.0'
        >>> _wcmatch_version('1')
        '4.1.0'

        :param str given_version: given version for compare, may be ``auto``
            (default), to select Unicode Version from Environment Variable,
            ``UNICODE_VERSION``. If the environment variable is not set, then the
            latest is used.
        :rtype: str
        :returns: unicode string, or non-unicode ``str`` type for python 2
            when given ``version`` is also type ``str``.
        """
        if given_version == 'auto':
            given_version = os.environ.get('UNICODE_VERSION', 'latest')

        if given_version == 'latest':
            return list_versions()[-1]

        supported_versions = list_versions()
        given_value = _wcversion_value(given_version)

        for version in reversed(supported_versions):
            if _wcversion_value(version) <= given_value:
                if version != given_version:
>                   warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
E                   UserWarning: Unicode version '999.0' not found, using '15.1.0'

wcwidth/wcwidth.py:234: UserWarning

test_ucslevel.py::test_nearest_999_unicode

test_ucslevel.py::test_nearest_999_unicode
def test_nearest_999_unicode():
        """wcwidth._wcmatch_version(u'999.0') returns nearest (latest)."""
        # given
        given, expected = u'999.0', wcwidth.list_versions()[-1]

        # exercise
>       result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:147: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

given_version = '999.0'

    @lru_cache(maxsize=8)
    def _wcmatch_version(given_version):
        """
        Return nearest matching supported Unicode version level.

        If an exact match is not determined, the nearest lowest version level is
        returned after a warning is emitted.  For example, given supported levels
        ``4.1.0`` and ``5.0.0``, and a version string of ``4.9.9``, then ``4.1.0``
        is selected and returned:

        >>> _wcmatch_version('4.9.9')
        '4.1.0'
        >>> _wcmatch_version('8.0')
        '8.0.0'
        >>> _wcmatch_version('1')
        '4.1.0'

        :param str given_version: given version for compare, may be ``auto``
            (default), to select Unicode Version from Environment Variable,
            ``UNICODE_VERSION``. If the environment variable is not set, then the
            latest is used.
        :rtype: str
        :returns: unicode string, or non-unicode ``str`` type for python 2
            when given ``version`` is also type ``str``.
        """
        if given_version == 'auto':
            given_version = os.environ.get('UNICODE_VERSION', 'latest')

        if given_version == 'latest':
            return list_versions()[-1]

        supported_versions = list_versions()
        given_value = _wcversion_value(given_version)

        for version in reversed(supported_versions):
            if _wcversion_value(version) <= given_value:
                if version != given_version:
>                   warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
E                   UserWarning: Unicode version '999.0' not found, using '15.1.0'

wcwidth/wcwidth.py:234: UserWarning

test_ucslevel.py::test_nonint_unicode

test_ucslevel.py::test_nonint_unicode
def test_nonint_unicode():
        """wcwidth._wcmatch_version(u'x.y.z') returns latest (unicode)."""
        # given
        given, expected = u'x.y.z', wcwidth.list_versions()[-1]
        warnings.resetwarnings()
        wcwidth._wcmatch_version.cache_clear()

        # exercise
        with pytest.warns(UserWarning):
            # warns that given version is not valid
>           result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:163: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:229: in _wcmatch_version
    given_value = _wcversion_value(given_version)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ver_string = 'x.y.z'

    @lru_cache(maxsize=128)
    def _wcversion_value(ver_string):
        """
        Integer-mapped value of given dotted version string.

        :param str ver_string: Unicode version string, of form ``n.n.n``.
        :rtype: tuple(int)
        :returns: tuple of digit tuples, ``tuple(int, [...])``.
        """
>       return tuple(map(int, ver_string.split('.')))
E       ValueError: invalid literal for int() with base 10: 'x'

wcwidth/wcwidth.py:194: ValueError

During handling of the above exception, another exception occurred:

    def test_nonint_unicode():
        """wcwidth._wcmatch_version(u'x.y.z') returns latest (unicode)."""
        # given
        given, expected = u'x.y.z', wcwidth.list_versions()[-1]
        warnings.resetwarnings()
        wcwidth._wcmatch_version.cache_clear()

        # exercise
>       with pytest.warns(UserWarning):
E       Failed: DID NOT WARN. No warnings of type (,) were emitted.
E        Emitted warnings: [].

tests/test_ucslevel.py:161: Failed

test_ucslevel.py::test_nonint_str

test_ucslevel.py::test_nonint_str
def test_nonint_str():
        """wcwidth._wcmatch_version(u'x.y.z') returns latest (str)."""
        # given
        given, expected = 'x.y.z', wcwidth.list_versions()[-1]
        warnings.resetwarnings()
        wcwidth._wcmatch_version.cache_clear()

        # exercise
        with pytest.warns(UserWarning):
            # warns that given version is not valid
>           result = wcwidth._wcmatch_version(given)

tests/test_ucslevel.py:179: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
wcwidth/wcwidth.py:229: in _wcmatch_version
    given_value = _wcversion_value(given_version)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

ver_string = 'x.y.z'

    @lru_cache(maxsize=128)
    def _wcversion_value(ver_string):
        """
        Integer-mapped value of given dotted version string.

        :param str ver_string: Unicode version string, of form ``n.n.n``.
        :rtype: tuple(int)
        :returns: tuple of digit tuples, ``tuple(int, [...])``.
        """
>       return tuple(map(int, ver_string.split('.')))
E       ValueError: invalid literal for int() with base 10: 'x'

wcwidth/wcwidth.py:194: ValueError

During handling of the above exception, another exception occurred:

    def test_nonint_str():
        """wcwidth._wcmatch_version(u'x.y.z') returns latest (str)."""
        # given
        given, expected = 'x.y.z', wcwidth.list_versions()[-1]
        warnings.resetwarnings()
        wcwidth._wcmatch_version.cache_clear()

        # exercise
>       with pytest.warns(UserWarning):
E       Failed: DID NOT WARN. No warnings of type (,) were emitted.
E        Emitted warnings: [].

tests/test_ucslevel.py:177: Failed

Patch diff

diff --git a/wcwidth/unicode_versions.py b/wcwidth/unicode_versions.py
index cc437d7..5c07028 100644
--- a/wcwidth/unicode_versions.py
+++ b/wcwidth/unicode_versions.py
@@ -15,4 +15,8 @@ def list_versions():
     :returns: Supported Unicode version numbers in ascending sorted order.
     :rtype: list[str]
     """
-    pass
+    return [
+        '4.1.0', '5.0.0', '5.1.0', '5.2.0', '6.0.0', '6.1.0', '6.2.0', '6.3.0',
+        '7.0.0', '8.0.0', '9.0.0', '10.0.0', '11.0.0', '12.0.0', '12.1.0',
+        '13.0.0', '14.0.0', '15.0.0', '15.1.0'
+    ]
diff --git a/wcwidth/wcwidth.py b/wcwidth/wcwidth.py
index 6d93a59..8daf7de 100644
--- a/wcwidth/wcwidth.py
+++ b/wcwidth/wcwidth.py
@@ -85,7 +85,22 @@ def _bisearch(ucs, table):
     :rtype: int
     :returns: 1 if ordinal value ucs is found within lookup table, else 0.
     """
-    pass
+    lbound = 0
+    ubound = len(table) - 1
+
+    if ucs < table[0][0] or ucs > table[-1][1]:
+        return 0
+
+    while ubound >= lbound:
+        mid = (lbound + ubound) // 2
+        if ucs > table[mid][1]:
+            lbound = mid + 1
+        elif ucs < table[mid][0]:
+            ubound = mid - 1
+        else:
+            return 1
+
+    return 0


 @lru_cache(maxsize=1000)
@@ -111,7 +126,26 @@ def wcwidth(wc, unicode_version='auto'):

     See :ref:`Specification` for details of cell measurement.
     """
-    pass
+    ucs = ord(wc)
+    
+    # C0/C1 control characters
+    if ucs < 32 or 0x07F <= ucs < 0x0A0:
+        return -1
+
+    # Check if the character is in the zero width table
+    if _bisearch(ucs, ZERO_WIDTH):
+        return 0
+
+    # Check if the character is in the wide East Asian table
+    if _bisearch(ucs, WIDE_EASTASIAN):
+        return 2
+
+    # Check if it's a combining character
+    if ucs in VS16_NARROW_TO_WIDE:
+        return 2
+
+    # All other characters are considered to have a width of 1
+    return 1


 def wcswidth(pwcs, n=None, unicode_version='auto'):
@@ -135,7 +169,17 @@ def wcswidth(pwcs, n=None, unicode_version='auto'):

     See :ref:`Specification` for details of cell measurement.
     """
-    pass
+    if n is None:
+        n = len(pwcs)
+    
+    width = 0
+    for char in pwcs[:n]:
+        char_width = wcwidth(char, unicode_version)
+        if char_width == -1:
+            return -1
+        width += char_width
+    
+    return width


 @lru_cache(maxsize=128)
@@ -147,7 +191,7 @@ def _wcversion_value(ver_string):
     :rtype: tuple(int)
     :returns: tuple of digit tuples, ``tuple(int, [...])``.
     """
-    pass
+    return tuple(map(int, ver_string.split('.')))


 @lru_cache(maxsize=8)
@@ -175,4 +219,21 @@ def _wcmatch_version(given_version):
     :returns: unicode string, or non-unicode ``str`` type for python 2
         when given ``version`` is also type ``str``.
     """
-    pass
+    if given_version == 'auto':
+        given_version = os.environ.get('UNICODE_VERSION', 'latest')
+    
+    if given_version == 'latest':
+        return list_versions()[-1]
+    
+    supported_versions = list_versions()
+    given_value = _wcversion_value(given_version)
+    
+    for version in reversed(supported_versions):
+        if _wcversion_value(version) <= given_value:
+            if version != given_version:
+                warnings.warn(f"Unicode version '{given_version}' not found, using '{version}'")
+            return version
+    
+    # If no suitable version found, return the earliest supported version
+    warnings.warn(f"Unicode version '{given_version}' not found, using '{supported_versions[0]}'")
+    return supported_versions[0]