diff --git a/docs/intro.rst b/docs/intro.rst index f70d515b..6fc71c96 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -654,6 +654,10 @@ languages: History ======= +0.8.2 *2026-06-29* + * **Bugfix** Do not raise IndexError when given legacy POSIX ``n`` argument to `wcswidth()`_ or + `wcstwidth()`_ exceed string length without raising IndexError + 0.8.1 *2026-06-08* * **Improved** `wcstwidth()`_ with new ``zeroer``, ``narrow_wider``, and ``narrow_zeroer`` Corrections_. `PR #226`_ diff --git a/pyproject.toml b/pyproject.toml index 10b0320f..249b0d53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = [ "hatchling" ] [project] name = "wcwidth" -version = "0.8.1" # don't forget to also update wcwidth/__init__.py:__version__ +version = "0.8.2" # don't forget to also update wcwidth/__init__.py:__version__ description = "Measures the displayed width of unicode strings in a terminal" readme = "README.rst" keywords = [ diff --git a/tests/test_core.py b/tests/test_core.py index 882b2eaf..9d500757 100755 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -497,6 +497,12 @@ def test_zwj_at_end_of_string(): assert wcwidth.wcswidth('a\u200D') == 1 +def test_wcswidth_n_exceeds_length(): + """Verify wcswidth() with n > len(string) does not raise IndexError.""" + assert wcwidth.wcswidth('hello', n=999) == 5 + assert wcwidth.wcswidth('\u30B3\u30F3', n=999) == 4 + + def test_soft_hyphen(): # Test SOFT HYPHEN, category 'Cf' usually are zero-width, but most # implementations agree to draw it was '1' cell, visually diff --git a/tests/test_term_overrides.py b/tests/test_term_overrides.py index 75f88052..0a7162ef 100644 --- a/tests/test_term_overrides.py +++ b/tests/test_term_overrides.py @@ -418,6 +418,13 @@ def test_get_term_overrides_narrow_wider_still_empty(): assert overrides.narrow_wider == () +@pytest.mark.parametrize('func', [wcwidth.wcstwidth, wcwidth.width]) +def test_narrow_wider_width(func): + """Verify width() & wcstwidth() match result of narrow_wider overrides.""" + assert wcwidth.wcswidth('\u261d') == 1 + assert func('\u261d', term_program='kitty') == 2 + + @pytest.mark.parametrize('codepoint', [ '\u00ad', '\u0600', diff --git a/wcwidth/__init__.py b/wcwidth/__init__.py index 2e619eb0..366911bd 100644 --- a/wcwidth/__init__.py +++ b/wcwidth/__init__.py @@ -78,4 +78,4 @@ # Using 'hatchling', it does not seem to provide the pyproject.toml nicety, "dynamic = ['version']" # like flit_core, maybe there is some better way but for now we have to duplicate it in both places # Prefer the installed distribution version when available (helps test environments) -__version__ = '0.8.1' # don't forget to also update pyproject.toml:version +__version__ = '0.8.2' # don't forget to also update pyproject.toml:version diff --git a/wcwidth/_wcswidth.py b/wcwidth/_wcswidth.py index 4b473fa7..32488d5b 100644 --- a/wcwidth/_wcswidth.py +++ b/wcwidth/_wcswidth.py @@ -98,7 +98,7 @@ def wcswidth( _wcwidth = wcwidth if ambiguous_width == 1 else lambda c: wcwidth(c, 'auto', ambiguous_width) - end = len(pwcs) if n is None else n + end = len(pwcs) if n is None else min(n, len(pwcs)) total_width = 0 idx = 0 @@ -262,7 +262,7 @@ def wcstwidth( # Select wcwidth call pattern for best lru_cache performance _wcwidth = wcwidth if ambiguous_width == 1 else lambda c: wcwidth(c, 'auto', ambiguous_width) - end = len(pwcs) if n is None else n + end = len(pwcs) if n is None else min(n, len(pwcs)) total_width = 0 idx = 0