GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Add wide-character (cchar_t) support to the curses module · Issue #151757 · python/cpython

Add wide-character (cchar_t) support to the curses module #151757

New issue
Copy link
New issue
Copy link
Closed
Closed
Add wide-character (cchar_t) support to the curses module#151757
Copy link
Labels
3.16new features, bugs and security fixesnew features, bugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement

Description

@serhiy-storchaka
serhiy-storchaka
opened on Jun 19, 2026
Issue body actions

The curses character-cell window methods currently accept only a single narrow character -- an int, a one-byte bytes, or a one-character str -- and most of the wide-character (cchar_t) part of the X/Open Curses API is unwrapped. A character cell can actually hold a full complex character: one spacing character optionally followed by combining characters. There is also no way to read text back as str rather than bytes, nor to query the wide erase/kill characters or get a printable representation of a wide character.

I propose to wrap the wide-character API, available when CPython is built against the wide-character ncursesw library.

The character-cell methods addch(), bkgd(), bkgdset(), border(), box(), echochar(), hline(), insch() and vline() additionally accept a str of a spacing character optionally followed by combining characters, dispatching to the ncursesw wide-character functions (wadd_wch(), wbkgrnd(), wborder_set(), wecho_wchar(), whline_set(), wins_wch(), wvline_set(), ...). border() and box() cannot mix integer or byte characters with wide-string characters in a single call. A cell is exactly one spacing character plus zero or more combining characters, so any extra spacing or control character (for example "ab", an emoji ZWJ sequence, or an emoji with a skin-tone modifier) is rejected with ValueError instead of being silently truncated by setcchar().

The new read methods window.get_wstr() and window.in_wstr() are the wide-character counterparts of window.getstr() and window.instr(), returning a str rather than a bytes object.

The new module functions curses.erasewchar(), curses.killwchar() and curses.wunctrl() are the wide-character counterparts of curses.erasechar(), curses.killchar() and curses.unctrl(), returning a str rather than a bytes object.

Linked PRs

  • gh-151757: Support wide and combining characters in the curses module #151758
  • gh-151757: Fix curses wide-character tests in non-UTF-8 locales #152062
  • gh-151757: Skip curses variation-selector test on older macOS #153344

Activity

  1. serhiy-storchaka
    added
    type-featureA feature request or enhancementA feature request or enhancement
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    3.16new features, bugs and security fixesnew features, bugs and security fixes
    on Jun 19, 2026
  2. asottile commented on Jun 23, 2026

    @asottile
    asottile
    on Jun 23, 2026
    Contributor
    More actions

    I gave up rebasing #17825 after 4 years -- any chance that's in scope too?

  3. serhiy-storchaka commented on Jun 23, 2026

    @serhiy-storchaka
    serhiy-storchaka
    on Jun 23, 2026
    MemberAuthor
    More actions

    Hmm, not exactly. It adds a sibling in_wstr() which returns a string. This solves the issue of wide characters, but does not cover all use all power of cchar_t -- characters+attributes+colors. I am planning to do this in separate issue. Most likely I'll introduce a special type for "string with attributes and colors". Adding in_wch() is postponed until finishing the design.

  4. serhiy-storchaka
    added a commit that references this issue on Jun 24, 2026

    gh-151757: Support wide and combining characters in the curses module (…

    a5677bf
  5. serhiy-storchaka
    closed this as completedon Jun 24, 2026
  6. encukou commented on Jun 24, 2026

    @encukou
    encukou
    on Jun 24, 2026
    Member
    More actions

    We now get these errors on ARM64 Raspbian 3.x:

    ======================================================================
    ERROR: test_addch_combining (test.test_curses.TestCurses.test_addch_combining)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/home/stan/buildarea/3.x.stan-raspbian.nondebug/build/Lib/test/test_curses.py", line 39, in wrapped
        test(self, *args, **kwargs)
        ~~~~^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/stan/buildarea/3.x.stan-raspbian.nondebug/build/Lib/test/test_curses.py", line 261, in test_addch_combining
        stdscr.addch('e\u0301')              # 'e' + COMBINING ACUTE ACCENT
        ~~~~~~~~~~~~^^^^^^^^^^^
    ValueError: a character cell must be a single spacing character optionally followed by combining characters
    ======================================================================
    ERROR: test_addch_emoji (test.test_curses.TestCurses.test_addch_emoji)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/home/stan/buildarea/3.x.stan-raspbian.nondebug/build/Lib/test/test_curses.py", line 39, in wrapped
        test(self, *args, **kwargs)
        ~~~~^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/stan/buildarea/3.x.stan-raspbian.nondebug/build/Lib/test/test_curses.py", line 282, in test_addch_emoji
        stdscr.addch(1, 0, '\u263a\ufe0f')        # WHITE SMILING FACE + VS-16
        ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
    ValueError: a character cell must be a single spacing character optionally followed by combining characters
    ======================================================================
    ERROR: test_wide_characters (test.test_curses.TestCurses.test_wide_characters)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/home/stan/buildarea/3.x.stan-raspbian.nondebug/build/Lib/test/test_curses.py", line 39, in wrapped
        test(self, *args, **kwargs)
        ~~~~^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/stan/buildarea/3.x.stan-raspbian.nondebug/build/Lib/test/test_curses.py", line 297, in test_wide_characters
        stdscr.echochar(combining)
        ~~~~~~~~~~~~~~~^^^^^^^^^^^
    ValueError: a character cell must be a single spacing character optionally followed by combining characters
    ----------------------------------------------------------------------
    
    Reacted by Serhiy Storchaka
  7. encukou
    reopened this on Jun 24, 2026
  8. serhiy-storchaka commented on Jun 24, 2026

    @serhiy-storchaka
    serhiy-storchaka
    on Jun 24, 2026
    MemberAuthor
    More actions

    Yes, this is locale-specific. I forgot to test this on non-UTF8-locales. There are precedences, I am working on this.

    Reacted by Petr Viktorin
  9. serhiy-storchaka
    added a commit that references this issue on Jun 24, 2026

    gh-151757: Fix curses wide-character tests in non-UTF-8 locales (GH-1…

    793f471
  10. serhiy-storchaka
    closed this as completedon Jun 24, 2026
  11. serhiy-storchaka
    added this to Curses issueson Jun 26, 2026
  12. github-project-automation
    moved this to Done in Curses issueson Jun 26, 2026
  13. weixlu commented on Jul 8, 2026

    @weixlu
    weixlu
    on Jul 8, 2026
    Contributor
    More actions

    @serhiy-storchaka Hi, I found that test_curses fails on macOS 14.2

    test test_curses failed -- Traceback (most recent call last):
      File "/Users/raodao/cpython/Lib/test/test_curses.py", line 67, in wrapped
        test(self, *args, **kwargs)
        ~~~~^^^^^^^^^^^^^^^^^^^^^^^
      File "/Users/raodao/cpython/Lib/test/test_curses.py", line 405, in test_addch_emoji
        stdscr.addch(1, 0, '\u263a\ufe0f')        # WHITE SMILING FACE + VS-16
        ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
    ValueError: a character cell must be a single spacing character optionally followed by up to 4 combining characters
    

    I did some investigation: Normally, wcwidth(VS16) == 0, for example on Ubuntu 24 and macOS 26. However, on macOS 14.2, wcwidth returns 1 for VS1–VS16.

    Do you think we should accommodate older versions of macOS here? If so, I’d be happy to open a PR. One possible fix would be to relax the check for bad in ConvertToWideCell().

  14. weixlu commented on Jul 8, 2026

    @weixlu
    weixlu
    on Jul 8, 2026
    Contributor
    More actions

    I'm running this

    import ctypes, ctypes.util
    libc = ctypes.CDLL(ctypes.util.find_library('c'))
    wcwidth = libc.wcwidth
    wcwidth.argtypes = [ctypes.c_wchar]
    wcwidth.restype = ctypes.c_int
    print('wcwidth(VS16):', wcwidth('\ufe0f'))
    

    And it print 1 rather than 0. I'm using Mac Studio 2023, with macOS Sonoma 14.2, Apple M2 Ultra

  15. serhiy-storchaka commented on Jul 8, 2026

    @serhiy-storchaka
    serhiy-storchaka
    on Jul 8, 2026
    MemberAuthor
    More actions

    Thank you for your report. We cannot relax the check in ConvertToWideCell(), because it is the same check that does ncurser. The only difference is that it silently truncates, while we report an error. The same check is used in complexstr_from_string() which splits string on cells. All three should be consistent to avoid producing garbage. They should also be consistent with the terminal emulator, which most likely uses system wcwidth().

    So, the only way is to skip this test on older macOS. #153344 does this. Please check that all other tests passed.

    Reacted by Xiaowei Lu
  16. serhiy-storchaka
    added a commit that references this issue on Jul 9, 2026

    gh-151757: Skip curses variation-selector test on older macOS (GH-153344

    f3fd9dc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.16new features, bugs and security fixesnew features, bugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement

    Projects

    • Curses issues
      Status
      Done
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.