Skip to content

Commit 4abf0d4

Browse files
gh-158078: Fix building curses with narrow ncurses (GH-158088)
Detect curses functions with a link check instead of a compile check, since the header can declare a function that the library lacks, for example term_attrs() in the narrow ncurses library. Fix test_curses on the narrow build. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent dc59006 commit 4abf0d4

3 files changed

Lines changed: 173 additions & 128 deletions

File tree

‎Lib/test/test_curses.py‎

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ def wrapped(self, *args, **kwargs):
4848

4949
WIDE_BUILD = import_module('_curses')._wide_character_support
5050

51+
def encodable(s, encoding):
52+
# Wide characters are only supported in a locale that can encode them.
53+
try:
54+
s.encode(encoding)
55+
except UnicodeEncodeError:
56+
return False
57+
return True
58+
59+
def storable(s, encoding):
60+
# Text the current build can place in character cells. A wide build
61+
# stores any locale-encodable text (combining sequences and multibyte
62+
# characters included). A narrow build has no wide-character cells, so
63+
# each character must occupy a single cell -- that is, encode to exactly
64+
# one byte.
65+
if not encodable(s, encoding):
66+
return False
67+
if WIDE_BUILD:
68+
return True
69+
return len(s.encode(encoding)) == len(s)
70+
5171
def requires_wide_build(test):
5272
@functools.wraps(test)
5373
def wrapped(self, *args, **kwargs):
@@ -387,24 +407,10 @@ def test_refresh_control(self):
387407
# combining sequence or a multibyte character are guarded with _storable().
388408

389409
def _encodable(self, s):
390-
# Wide characters are only supported in a locale that can encode them.
391-
try:
392-
s.encode(self.stdscr.encoding)
393-
except UnicodeEncodeError:
394-
return False
395-
return True
410+
return encodable(s, self.stdscr.encoding)
396411

397412
def _storable(self, s):
398-
# Text the current build can place in character cells. A wide build
399-
# stores any locale-encodable text (combining sequences and multibyte
400-
# characters included). A narrow build has no wide-character cells, so
401-
# each character must occupy a single cell -- that is, encode to exactly
402-
# one byte.
403-
if not self._encodable(s):
404-
return False
405-
if WIDE_BUILD:
406-
return True
407-
return len(s.encode(self.stdscr.encoding)) == len(s)
413+
return storable(s, self.stdscr.encoding)
408414

409415
def _char_code(self, ch):
410416
# The integer the int-input API (addch(int), do_command()) uses for a
@@ -1124,7 +1130,11 @@ def test_cell_null_char(self):
11241130
# A cell holding a NUL reads back as the cell that writes it.
11251131
win = curses.newwin(3, 8, 0, 0)
11261132
win.insch(0, 0, '\0')
1127-
self.assertEqual(win.in_wch(0, 0), cell)
1133+
if WIDE_BUILD:
1134+
self.assertEqual(win.in_wch(0, 0), cell)
1135+
else:
1136+
# A narrow build inserts a NUL as "^@".
1137+
self.assertEqual(str(win.in_wch(0, 0)), '^')
11281138
# A string of cells cannot hold a NUL: it would end a batch write.
11291139
self.assertRaises(ValueError, curses.complexstr, 'a\0b')
11301140
self.assertRaises(ValueError, curses.complexstr, '\0')
@@ -3593,10 +3603,8 @@ def test_set_and_label(self):
35933603
def test_set_wide(self):
35943604
screen = self.make_slk_screen()
35953605
label = 'Ångström'
3596-
try:
3597-
label.encode(screen.stdscr.encoding)
3598-
except UnicodeEncodeError:
3599-
self.skipTest('the locale cannot encode %r' % label)
3606+
if not storable(label, screen.stdscr.encoding):
3607+
self.skipTest('cannot store %r in this locale' % label)
36003608
curses.slk_set(1, label, 0)
36013609
# The label can be truncated to fit the soft label width, e.g. in the
36023610
# EUC-JP locale, where "Å" and "ö" are double-width JIS X 0212

0 commit comments

Comments
 (0)