diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 5db44ebedd2ee0a..0eb43af795cdf80 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -1138,6 +1138,8 @@ def test_cell_null_char(self): # A string of cells cannot hold a NUL: it would end a batch write. self.assertRaises(ValueError, curses.complexstr, 'a\0b') self.assertRaises(ValueError, curses.complexstr, '\0') + self.assertRaises(ValueError, curses.complexstr, ['a', '\0', 'b']) + self.assertRaises(ValueError, curses.complexstr, [cell, 'b']) def test_add_string_behavior(self): # addstr() advances the cursor past the written text; addnstr() diff --git a/Misc/NEWS.d/next/Library/2026-11-03-17-54-01.gh-issue-155877.KPDGNV.rst b/Misc/NEWS.d/next/Library/2026-11-03-17-54-01.gh-issue-155877.KPDGNV.rst new file mode 100644 index 000000000000000..93fc4954e52d1d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-03-17-54-01.gh-issue-155877.KPDGNV.rst @@ -0,0 +1,2 @@ +:class:`curses.complexstr` now rejects a NUL item in an iterable, as it +already did for a NUL in a string. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 401b777be23caa6..a741b7754d0542e 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1300,15 +1300,35 @@ curses_pack_cell(cursesmodule_state *state, PyObject *item, curses_cell_t *out) { if (Py_IS_TYPE(item, state->complexchar_type)) { *out = _PyCursesComplexCharObject_CAST(item)->cval; - return 0; } - if (PyUnicode_Check(item)) { - return curses_cell_pack(state, out, item, A_NORMAL, 0, "complexstr"); + else if (PyUnicode_Check(item)) { + if (curses_cell_pack(state, out, item, A_NORMAL, 0, "complexstr") < 0) { + return -1; + } } - PyErr_Format(PyExc_TypeError, - "complexstr cell must be a complexchar or a str, not %T", - item); - return -1; + else { + PyErr_Format(PyExc_TypeError, + "complexstr cell must be a complexchar or a str, not %T", + item); + return -1; + } +#ifdef HAVE_NCURSESW + wchar_t wstr[CCHARW_MAX + 1]; + attr_t attr; + int pair; + if (curses_getcchar(out, wstr, &attr, &pair) == ERR) { + PyErr_SetString(state->error, "getcchar() returned ERR"); + return -1; + } + int nul = wstr[0] == L'\0'; +#else + int nul = (*out & A_CHARTEXT) == 0; +#endif + if (nul) { + PyErr_SetString(PyExc_ValueError, "embedded null character"); + return -1; + } + return 0; } /* Wrap a buffer of len cells in a new complexstr, copying them in. tp_alloc