Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions Lib/curses/textpad.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,16 @@ def gather(self):
result = ""
self._update_max_yx()
for y in range(self.maxy+1):
self.win.move(y, 0)
stop = self._end_of_line(y)
if stop == 0 and self.stripspaces:
continue
for x in range(self.maxx+1):
if self.stripspaces and x > stop:
break
result = result + str(self.win.in_wch(y, x))
# The whole line: in_wstr() reads a double-width character once,
# skipping the continuation cell that holds its other half.
line = self.win.in_wstr(y, 0)
if self.stripspaces:
stripped = line.rstrip(' ')
if not stripped:
continue
# Keep the blank the cursor rests on past the last character.
line = stripped + ' ' if len(stripped) < len(line) else stripped
result = result + line
if self.maxy > 0:
result = result + "\n"
return result
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2694,6 +2694,23 @@ def test_textbox_combining(self):
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ')

@requires_wide_build
def test_textbox_double_width(self):
# A double-width (East Asian) character occupies two cells. gather()
# reads a whole line at a time so that the second cell, which holds
# the same character, is not reported as another one.
text = '你好'
if not self._encodable(text):
self.skipTest('the locale cannot encode %r' % text)
box, win = self._make_textbox(1, 12)
for ch in text:
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ')
box, win = self._make_textbox(1, 12, stripspaces=False)
for ch in text:
box.do_command(ch)
self.assertEqual(box.gather(), text + ' ' * 8)

def test_textbox_edit_wide(self):
# edit() reads characters through get_wch(). Each character is pushed
# with unget_wch(), which on a narrow build requires it to encode to a
Expand Down Expand Up @@ -2967,6 +2984,11 @@ def test_has_extended_color_support(self):
r = curses.has_extended_color_support()
self.assertIsInstance(r, bool)

def test_err_and_ok(self):
# ERR is negative; it is not a chtype constant.
self.assertEqual(curses.ERR, -1)
self.assertEqual(curses.OK, 0)

def test_type_names(self):
# The curses types report their public module rather than the
# underscore extension that implements them.
Expand Down
29 changes: 22 additions & 7 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -6657,11 +6657,10 @@ curses_init_dict(PyObject *module)
}
/* This was moved from initcurses() because it core dumped on SGI,
where they're not defined until you've called initscr() */
/* Use long long, not long: a chtype constant (the A_* attributes, ACS_*
and key codes) can set bits beyond a 32-bit long, which is what long is
on LLP64 platforms such as Windows -- A_DIM (0x80000000) would otherwise
be sign-extended to a negative number. long long is at least 64 bits
everywhere and still represents the negative ERR (-1). */
/* Use unsigned long long, not long: a chtype constant (the A_* attributes,
ACS_* and key codes) can set bits beyond a 32-bit long, which is what
long is on LLP64 platforms such as Windows -- A_DIM (0x80000000) would
otherwise be sign-extended to a negative number. */
#define SetDictInt(NAME, VALUE) \
do { \
PyObject *value = PyLong_FromUnsignedLongLong((unsigned long long)(VALUE)); \
Expand Down Expand Up @@ -9419,8 +9418,24 @@ cursesmodule_exec(PyObject *module)
} \
} while (0)

SetDictInt("ERR", ERR);
SetDictInt("OK", OK);
/* ERR is -1, so it needs a signed conversion, unlike the chtype
constants below. */
#define SetDictSignedInt(NAME, VALUE) \
do { \
PyObject *value = PyLong_FromLongLong((long long)(VALUE)); \
if (value == NULL) { \
return -1; \
} \
int rc = PyDict_SetItemString(module_dict, (NAME), value); \
Py_DECREF(value); \
if (rc < 0) { \
return -1; \
} \
} while (0)

SetDictSignedInt("ERR", ERR);
SetDictSignedInt("OK", OK);
#undef SetDictSignedInt

/* Here are some attributes you can add to chars to print */

Expand Down
2 changes: 1 addition & 1 deletion Platforms/emscripten/config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Any data that can vary between Python versions is to be kept in this file.
# This allows for blanket copying of the Emscripten build code between supported
# Python versions.
emscripten-version = "6.0.6"
emscripten-version = "6.0.8"
node-version = "24"
test-args = [
"-m", "test",
Expand Down
Loading