configure decides whether to build the curses module wide (HAVE_NCURSESW, the cchar_t API) from the pkg-config name: ncursesw is wide, ncurses is narrow. But wide-character support is a property of the library, not of its name. An ncurses built with --enable-widec that keeps the plain libncurses/ncurses.pc name is built narrow though it is fully wide-capable. This happens with pkgsrc ncurses (NetBSD, illumos and others) and with the system ncurses of macOS.
A narrow build against a wide library loses functionality. The wide API (get_wch(), add_wch(), complexchar, ...) is not built at all. And the character-based API (addch(), getch(), inch(), getbkgd(), ...) handles only ASCII in multibyte locales such as UTF-8, which are now the norm; the single-byte 8-bit locales in which it works fully are rare today.
configure already probes the wide API (setcchar()/add_wch()) for the native curses backend (gh-136687). Running that probe for the ncurses/auto backends too, and defining HAVE_NCURSESW from the probe result rather than from the name, builds wide whenever the library supports it.
On macOS, where the default locale is UTF-8, the narrow build is almost unusable, so this is a bugfix as much as an improvement: curses moves from a narrow build to a wide one. The system ncurses is wide-capable and the existing Darwin block already adds -D_XOPEN_SOURCE_EXTENDED reaching for wide, so this completes that intent.
@hugovk, please take a look. This can be considered a bugfix, because narrow build is almost unusable on UTF-8 locales. But this is a non-trivial build change, so I leave the question about backporting on you.
It is even worse. There are bugs in some narrow API functions in wide ncurses build which make them unusable on non-Latin1 locale. We can work around them (see #153862 and #153864), but this only works if the wide build was detected. Without this fix, other fixes do not have effect.
caveat on "wide by capability": the probe here keys off the write side (setcchar()/add_wch()), but the cchar_t cell API also needs the read side (getcchar()), and those can disagree. concretely, ncursesw 6.1 (that we have on CentOS servers) passes the write probe (and has_extended_color_support() is true) yet fails every cell read.
repro:
>>>importcurses>>>c=curses.complexchar('A') # construction (setcchar) succeeds>>>str(c) # but every read-back (getcchar) raises:curses.error: getcchar() returnedERR
this takes down in_wch() / in_wchstr() / getbkgrnd(), complexchar.attr / .pair, and since gh-153395, the curses.ascii predicates that str() a complexchar, so test_curses fails even headless (TestAscii.test_complexchar isn't $TERM-gated).
I'm not sure this is directly applicable or related to this issue, so let me know if I should file a separate issue (or not, if ncrusesw 6.1 is just considered unsupported)
Thanks @itamaro -- real bug, and separate from this issue's detection: ncursesw 6.1/6.2 build wide by name, not the probe. curses_getcchar() reads the pair through getcchar()'s opts slot, which getcchar() rejected before ncurses 6.3 (patch 20210116) -- so every wide read failed (setcchar() accepts opts, hence construction works). Filed + fixed: gh-154525 / gh-154526 (read via the short slot on <6.3). Verified against ncursesw 5.9-6.3: affects 6.1/6.2 only -- supported, not dropped.
Heads-up: 6.1 also fails test_dynamic_color_pairs, unrelated (ncurses find_pair() was broken before 6.3) -- separate follow-up.
Activity
serhiy-storchaka commented on Jul 19, 2026
@hugovk, please take a look. This can be considered a bugfix, because narrow build is almost unusable on UTF-8 locales. But this is a non-trivial build change, so I leave the question about backporting on you.
serhiy-storchaka commented on Jul 19, 2026
It is even worse. There are bugs in some narrow API functions in wide ncurses build which make them unusable on non-Latin1 locale. We can work around them (see #153862 and #153864), but this only works if the wide build was detected. Without this fix, other fixes do not have effect.
itamaro commented on Jul 22, 2026
caveat on "wide by capability": the probe here keys off the write side (setcchar()/add_wch()), but the cchar_t cell API also needs the read side (getcchar()), and those can disagree. concretely, ncursesw 6.1 (that we have on CentOS servers) passes the write probe (and has_extended_color_support() is true) yet fails every cell read.
repro:
this takes down in_wch() / in_wchstr() / getbkgrnd(), complexchar.attr / .pair, and since gh-153395, the curses.ascii predicates that str() a complexchar, so test_curses fails even headless (TestAscii.test_complexchar isn't $TERM-gated).
I'm not sure this is directly applicable or related to this issue, so let me know if I should file a separate issue (or not, if ncrusesw 6.1 is just considered unsupported)
serhiy-storchaka commented on Jul 23, 2026
Thanks @itamaro -- real bug, and separate from this issue's detection: ncursesw 6.1/6.2 build wide by name, not the probe. curses_getcchar() reads the pair through getcchar()'s opts slot, which getcchar() rejected before ncurses 6.3 (patch 20210116) -- so every wide read failed (setcchar() accepts opts, hence construction works). Filed + fixed: gh-154525 / gh-154526 (read via the short slot on <6.3). Verified against ncursesw 5.9-6.3: affects 6.1/6.2 only -- supported, not dropped.
Heads-up: 6.1 also fails test_dynamic_color_pairs, unrelated (ncurses find_pair() was broken before 6.3) -- separate follow-up.
pythongh-154070: Don't leak curses CPPFLAGS into the whole-interprete…
gh-154070: Build the curses module wide by capability, not by name (G…
[3.15] gh-154070: Build the curses module wide by capability, not by …
serhiy-storchaka commented on Jul 28, 2026
Tests passed on all relevant buildbots on main and 3.15.
[3.14] gh-154070: Build the curses module wide by capability, not by …
[3.13] gh-154070: Build the curses module wide by capability, not by …
pythongh-154070: Build the curses module wide by capability, not by n…