Skip to content

gh-153864: Fix curses window.insch() for non-ASCII characters on a wide build#153865

Merged
serhiy-storchaka merged 2 commits into
python:mainfrom
serhiy-storchaka:curses-insch-locale-decode
Jul 19, 2026
Merged

gh-153864: Fix curses window.insch() for non-ASCII characters on a wide build#153865
serhiy-storchaka merged 2 commits into
python:mainfrom
serhiy-storchaka:curses-insch-locale-decode

Conversation

@serhiy-storchaka

@serhiy-storchaka serhiy-storchaka commented Jul 17, 2026

Copy link
Copy Markdown
Member

On a wide (ncursesw) build, window.insch() and window.mvinsch() insert a non-ASCII int/bytes byte as its Latin-1 code point instead of decoding it through the window's encoding, unlike addch() — e.g. insch(0xA4) under ISO-8859-15 inserts ¤ (U+00A4) while addch(0xA4) inserts .

The cause is in ncurses: winsch() has a fast path for a printable 8-bit byte that stores it directly as a code point, bypassing the locale decode waddch() performs. The fix decodes the byte with btowc() (the inverse of inch()'s wctob()) and inserts it with wins_wch(); a byte with no single-byte form in the locale falls back to winsch().

The underlying ncurses behavior was reported upstream.

…n a wide build

On a wide build, winsch() does not locale-decode a byte above 127, unlike
waddch(), so insch() inserted '¤' (U+00A4) instead of '€' for byte 0xA4
under ISO-8859-15.  Decode the byte with btowc() and insert it as a wide
character, like addch().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread Modules/_cursesmodule.c Outdated
Comment thread Modules/_cursesmodule.c Outdated
@serhiy-storchaka
serhiy-storchaka enabled auto-merge (squash) July 19, 2026 07:45
@serhiy-storchaka
serhiy-storchaka merged commit 223cbff into python:main Jul 19, 2026
98 of 100 checks passed
@bedevere-app

bedevere-app Bot commented Jul 19, 2026

Copy link
Copy Markdown

GH-154117 is a backport of this pull request to the 3.15 branch.

serhiy-storchaka added a commit that referenced this pull request Jul 19, 2026
…on a wide build (GH-153865) (GH-154117)

On a wide build, winsch() does not locale-decode a byte above 127, unlike
waddch(), so insch() inserted '¤' (U+00A4) instead of '€' for byte 0xA4
under ISO-8859-15.  Decode the byte with btowc() and insert it as a wide
character, like addch().

(cherry picked from commit 223cbff)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@bedevere-app

bedevere-app Bot commented Jul 19, 2026

Copy link
Copy Markdown

GH-154152 is a backport of this pull request to the 3.14 branch.

@bedevere-app

bedevere-app Bot commented Jul 19, 2026

Copy link
Copy Markdown

GH-154154 is a backport of this pull request to the 3.13 branch.

serhiy-storchaka added a commit that referenced this pull request Jul 19, 2026
…on a wide build (GH-153865) (GH-154154)

On a wide build, winsch() does not locale-decode a byte above 127, unlike
waddch(), so insch()/mvinsch() stored the wrong character for a non-ASCII
byte of an 8-bit locale ('€' 0xA4 under ISO-8859-15 became U+00A4 '¤').
Decode the byte with btowc() and insert it as a wide character, as addch()
effectively does.

(cherry picked from commit 223cbff)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
serhiy-storchaka added a commit that referenced this pull request Jul 19, 2026
…on a wide build (GH-153865) (GH-154152)

On a wide build, winsch() does not locale-decode a byte above 127, unlike
waddch(), so insch()/mvinsch() stored the wrong character for a non-ASCII
byte of an 8-bit locale ('€' 0xA4 under ISO-8859-15 became U+00A4 '¤').
Decode the byte with btowc() and insert it as a wide character, as addch()
effectively does.

(cherry picked from commit 223cbff)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants