gh-156230: Bound a curses window read by the window, not by 2047 (GH-156282)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/ca79981cc08f23990e2f00d78d84681e40f369bd
commit: ca79981cc08f23990e2f00d78d84681e40f369bd
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-23T19:53:59+03:00
summary:

gh-156230: Bound a curses window read by the window, not by 2047 (GH-156282)

instr(), in_wstr() and in_wchstr() clamped the count to 2047 and silently
truncated a longer line, which a pad can have.  A window read cannot return
more than the columns left on the line, in the unit each method counts: cells,
characters, or bytes at CCHARW_MAX characters of MB_CUR_MAX bytes per cell.
Cap the count by that, and read the rest of the line when the count is
omitted, which it now can be.

getstr() and get_wstr() read the keyboard rather than the window, so their
limit stays.

files:
A Misc/NEWS.d/next/Library/2026-08-22-14-12-40.gh-issue-156230.Lt9wQd.rst
M Doc/library/curses.rst
M Lib/test/test_curses.py
M Modules/_cursesmodule.c
M Modules/clinic/_cursesmodule.c.h

diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index d6bccbb730f8b4..858371f927f4fa 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -1404,26 +1404,29 @@ Reading window contents
             window.instr(y, x[, n])
 
    Read the text of the window from the current cursor position,
-   or from *y*, *x* if specified, to the end of the line,
+   or from *y*, *x* if specified, to the end of the line
+   or at most *n* bytes if *n* is specified,
    and return it as a bytes object, in the encoding of the current locale.
    Attributes and color pairs are stripped;
    use :meth:`in_wchstr` to read them too.
-   At most *n* bytes are read; *n* defaults to and cannot exceed 2047.
    A character not representable in the encoding cannot be returned;
    use :meth:`in_wstr` for those.
 
    .. versionchanged:: 3.14
       The maximum value for *n* was increased from 1023 to 2047.
 
+   .. versionchanged:: next
+      *n* is no longer limited to 2047.
+
 .. method:: window.in_wstr([n])
             window.in_wstr(y, x[, n])
 
    Read the text of the window from the current cursor position,
-   or from *y*, *x* if specified, to the end of the line,
+   or from *y*, *x* if specified, to the end of the line
+   or at most *n* characters if *n* is specified,
    and return it as a :class:`str`.
    Attributes and color pairs are stripped;
    use :meth:`in_wchstr` to read them too.
-   At most *n* characters are read; *n* defaults to and cannot exceed 2047.
 
    This is the wide-character variant of :meth:`instr`.
 
@@ -1433,12 +1436,12 @@ Reading window contents
             window.in_wchstr(y, x[, n])
 
    Read the styled cells of the window from the current cursor position,
-   or from *y*, *x* if specified, to the end of the line,
+   or from *y*, *x* if specified, to the end of the line
+   or at most *n* cells if *n* is specified,
    and return them as a :class:`complexstr`.
    Unlike :meth:`instr` and :meth:`in_wstr`, each cell keeps its attributes
    and color pair, so the result can be written back unchanged
    with :meth:`addstr`.
-   At most *n* cells are read; *n* defaults to and cannot exceed 2047.
 
    .. versionadded:: next
 
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index ea2dcd76b585a9..630de544a457f4 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -672,6 +672,11 @@ def test_in_wstr(self):
         stdscr.addstr(0, 0, 'abz')
         self.assertEqual(stdscr.in_wstr(0, 0, 0), '')
         self.assertEqual(stdscr.in_wstr(0), '')
+        self.assertEqual(stdscr.in_wstr(0, 0, 2**31), stdscr.in_wstr(0, 0))
+        self.assertRaises(OverflowError, stdscr.in_wstr, 2**1000)
+        self.assertRaises(ValueError, stdscr.in_wstr, -2)
+        self.assertRaises(ValueError, stdscr.in_wstr, 0, 2, -2)
+        self.assertRaises(ValueError, stdscr.in_wstr, -2**1000)
 
     def test_complexchar(self):
         # A complexchar is a styled wide-character cell: str() is its text,
@@ -871,6 +876,11 @@ def test_in_wchstr(self):
         # The count is optional and reads to the end of the line by default.
         stdscr.move(0, 0)
         self.assertEqual(str(stdscr.in_wchstr())[:3], 'AbC')
+        self.assertEqual(stdscr.in_wchstr(0, 0, 2**31), stdscr.in_wchstr(0, 0))
+        self.assertRaises(OverflowError, stdscr.in_wchstr, 2**1000)
+        self.assertRaises(ValueError, stdscr.in_wchstr, -2)
+        self.assertRaises(ValueError, stdscr.in_wchstr, 0, 2, -2)
+        self.assertRaises(ValueError, stdscr.in_wchstr, -2**1000)
 
     def test_complexstr_in_write_methods(self):
         # addstr/addnstr/insstr/insnstr also accept a complexstr, written via
@@ -1188,8 +1198,13 @@ def test_read_from_window(self):
         self.assertEqual(stdscr.instr(3)[:6], b' AB')
         self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ')
         self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ')
+        # A huge count is bounded by the line, and is not used to size the
+        # read buffer.
+        self.assertEqual(stdscr.instr(0, 0, 2**31), stdscr.instr(0, 0))
+        self.assertRaises(OverflowError, stdscr.instr, 2**1000)
         self.assertRaises(ValueError, stdscr.instr, -2)
         self.assertRaises(ValueError, stdscr.instr, 0, 2, -2)
+        self.assertRaises(ValueError, stdscr.instr, -2**1000)
         # instr(y, x, 1) reads a single cell byte, so only a character that the
         # window encoding maps to one byte is checked.  inch() returns the cell
         # value, which is the locale byte.
@@ -1206,6 +1221,25 @@ def test_read_from_window(self):
                 self.assertEqual(stdscr.instr(2, 0, 1), b)
                 self.assertEqual(stdscr.inch(2, 0), v)
 
+    def test_read_long_line(self):
+        # A pad line can be longer than a window, and a character can be
+        # encoded with several bytes, so instr() can read more bytes than
+        # there are cells.  See _encodable for the character set.
+        width = 3000
+        pad = curses.newpad(1, width)
+        for ch in ['z', '\u00e9', '\u20ac', '\u0434', '\uff71']:
+            if not self._storable(ch):
+                continue
+            pad.addstr(0, 0, ch)
+            if pad.getyx()[1] != 1:
+                continue        # a wide character occupies two cells
+            with self.subTest(ch=ch):
+                line = ch * (width - 1) + ' '   # the last cell is left blank
+                pad.addstr(0, 0, line[:-1])
+                self.assertEqual(pad.instr(0, 0), line.encode(pad.encoding))
+                self.assertEqual(pad.in_wstr(0, 0), line)
+                self.assertEqual(str(pad.in_wchstr(0, 0)), line)
+
     def test_coordinate_errors(self):
         # Addressing a cell outside the window raises curses.error.
         win = curses.newwin(5, 10, 0, 0)
diff --git a/Misc/NEWS.d/next/Library/2026-08-22-14-12-40.gh-issue-156230.Lt9wQd.rst b/Misc/NEWS.d/next/Library/2026-08-22-14-12-40.gh-issue-156230.Lt9wQd.rst
new file mode 100644
index 00000000000000..3b9b41c389580e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-22-14-12-40.gh-issue-156230.Lt9wQd.rst
@@ -0,0 +1,3 @@
+:meth:`curses.window.instr`, :meth:`~curses.window.in_wstr` and
+:meth:`~curses.window.in_wchstr` no longer limit the count to 2047, which
+silently truncated a longer line.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 45ba6476bbc4a6..12d7664b395967 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -3535,6 +3535,29 @@ _curses_window_get_wch_impl(PyCursesWindowObject *self, int group_right_1,
 #endif
 }
 
+/* Characters one cell can hold. */
+#ifdef HAVE_NCURSESW
+#define CURSES_CELL_CHARS       CCHARW_MAX
+#else
+#define CURSES_CELL_CHARS       1
+#endif
+
+/* The columns left on the line, in the unit the caller counts. */
+static unsigned int
+curses_window_read_limit(PyCursesWindowObject *self, int use_xy, int x,
+                         unsigned int per_cell)
+{
+    int col = use_xy ? x : getcurx(self->win);
+    int maxx = getmaxx(self->win);
+    if (col < 0) {
+        col = 0;
+    }
+    if (col > maxx) {
+        return 0;
+    }
+    return ((unsigned int)(maxx - col) + 1) * per_cell;
+}
+
 /* Read user input into a new bytes object (empty on ERR), with primitive line
    editing.  Shared by getstr() and, without the wide library, by get_wstr(). */
 static PyObject *
@@ -3828,15 +3851,15 @@ _curses_window_inch_impl(PyCursesWindowObject *self, int group_right_1,
    with attributes and color stripped.  Shared by instr() and, without the wide
    library, by in_wstr(). */
 static PyObject *
-curses_window_instr_bytes(PyCursesWindowObject *self, int use_xy,
+curses_window_instr_bytes(PyCursesWindowObject *self, int use_xy, int use_n,
                           int y, int x, unsigned int n)
 {
     int rtn;
-    unsigned int max_buf_size = 2048;
-
-    n = Py_MIN(n, max_buf_size - 1 - CURSES_STR_EXTRA);
-    n += CURSES_STR_EXTRA;
-    PyBytesWriter *writer = PyBytesWriter_Create(n + 1);
+    unsigned int limit = curses_window_read_limit(self, use_xy, x,
+                                                  CURSES_CELL_CHARS
+                                                  * (unsigned int)MB_CUR_MAX);
+    unsigned int nread = use_n ? Py_MIN(n, limit) : limit;
+    PyBytesWriter *writer = PyBytesWriter_Create(nread + CURSES_STR_EXTRA + 1);
     if (writer == NULL) {
         return NULL;
     }
@@ -3844,14 +3867,14 @@ curses_window_instr_bytes(PyCursesWindowObject *self, int use_xy,
 
     /* Read again if the library stored more than asked: truncating could
        split a multibyte character. */
-    for (unsigned int want = n - CURSES_STR_EXTRA; ; n = want) {
+    for (unsigned int ask = nread + CURSES_STR_EXTRA; ; ask = nread) {
         if (use_xy) {
-            rtn = mvwinnstr(self->win, y, x, buf, n);
+            rtn = mvwinnstr(self->win, y, x, buf, ask);
         }
         else {
-            rtn = winnstr(self->win, buf, n);
+            rtn = winnstr(self->win, buf, ask);
         }
-        if (rtn == ERR || (unsigned int)rtn <= want) {
+        if (rtn == ERR || (unsigned int)rtn <= nread) {
             break;
         }
     }
@@ -3872,24 +3895,27 @@ _curses.window.instr
     x: int
         X-coordinate.
     ]
-    n: unsigned_int = 2047
-        Maximal number of bytes.
+    [
+    n: unsigned_int
+        Maximal number of bytes.  The rest of the line by default.
+    ]
     /
 
 Return the text of the window as a bytes object.
 
 Read from the current cursor position, or from y, x if specified, to
-the end of the line, and return the text in the encoding of the
-current locale, with attributes and color pairs stripped.  At most n
-bytes are read.
+the end of the line or at most n bytes if n is specified, and return
+the text in the encoding of the current locale, with attributes and
+color pairs stripped.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_window_instr_impl(PyCursesWindowObject *self, int group_left_1,
-                          int y, int x, unsigned int n)
-/*[clinic end generated code: output=40081f67070132da input=4ece6af75b09346f]*/
+_curses_window_instr_impl(PyCursesWindowObject *self, int group_right_1,
+                          int y, int x, int group_right_2, unsigned int n)
+/*[clinic end generated code: output=2428948b44ad10c7 input=9307eca4bd576899]*/
 {
-    return curses_window_instr_bytes(self, group_left_1, y, x, n);
+    return curses_window_instr_bytes(self, group_right_1, group_right_2,
+                                     y, x, n);
 }
 
 /*[clinic input]
@@ -3987,44 +4013,45 @@ _curses.window.in_wstr
     x: int
         X-coordinate.
     ]
-    n: unsigned_int = 2047
-        Maximal number of characters.
+    [
+    n: unsigned_int
+        Maximal number of characters.  The rest of the line by default.
+    ]
     /
 
 Return the text of the window as a str.
 
 This is the wide-character variant of instr().  Read from the
 current cursor position, or from y, x if specified, to the end of
-the line, with attributes and color pairs stripped.  At most n
-characters are read.
+the line or at most n characters if n is specified, with attributes
+and color pairs stripped.
 [clinic start generated code]*/
 
 static PyObject *
-_curses_window_in_wstr_impl(PyCursesWindowObject *self, int group_left_1,
-                            int y, int x, unsigned int n)
-/*[clinic end generated code: output=e3db72a1f10b9875 input=436737264c54d8d3]*/
+_curses_window_in_wstr_impl(PyCursesWindowObject *self, int group_right_1,
+                            int y, int x, int group_right_2, unsigned int n)
+/*[clinic end generated code: output=d8c8bcfe8a26f519 input=5ba908338a94bfc8]*/
 {
 #ifdef HAVE_NCURSESW
     int rtn;
-    unsigned int max_buf_size = 2048;
-
-    n = Py_MIN(n, max_buf_size - 1 - CURSES_STR_EXTRA);
-    n += CURSES_STR_EXTRA;
-    wchar_t *buf = PyMem_New(wchar_t, n + 1);
+    unsigned int limit = curses_window_read_limit(self, group_right_1, x,
+                                                  CURSES_CELL_CHARS);
+    unsigned int nread = group_right_2 ? Py_MIN(n, limit) : limit;
+    wchar_t *buf = PyMem_New(wchar_t, nread + CURSES_STR_EXTRA + 1);
     if (buf == NULL) {
         return PyErr_NoMemory();
     }
 
     /* Read again if the library stored more than asked: truncating could
        separate a combining character from its base. */
-    for (unsigned int want = n - CURSES_STR_EXTRA; ; n = want) {
-        if (group_left_1) {
-            rtn = mvwinnwstr(self->win, y, x, buf, n);
+    for (unsigned int ask = nread + CURSES_STR_EXTRA; ; ask = nread) {
+        if (group_right_1) {
+            rtn = mvwinnwstr(self->win, y, x, buf, ask);
         }
         else {
-            rtn = winnwstr(self->win, buf, n);
+            rtn = winnwstr(self->win, buf, ask);
         }
-        if (rtn == ERR || (unsigned int)rtn <= want) {
+        if (rtn == ERR || (unsigned int)rtn <= nread) {
             break;
         }
     }
@@ -4039,7 +4066,8 @@ _curses_window_in_wstr_impl(PyCursesWindowObject *self, int group_left_1,
 #else
     /* Without the wide library, read the bytes as instr() does and decode them
        with the window's encoding. */
-    PyObject *bytes = curses_window_instr_bytes(self, group_left_1, y, x, n);
+    PyObject *bytes = curses_window_instr_bytes(self, group_right_1,
+                                               group_right_2, y, x, n);
     if (bytes == NULL) {
         return NULL;
     }
@@ -4060,43 +4088,46 @@ _curses.window.in_wchstr
     x: int
         X-coordinate.
     ]
-    n: unsigned_int = 2047
-        Maximal number of cells.
+    [
+    n: unsigned_int
+        Maximal number of cells.  The rest of the line by default.
+    ]
     /
 
 Return the styled cells of the window as a complexstr.
 
-Read from the current cursor position, or from y, x if specified, to
-the end of the line.  Unlike instr() and in_wstr(), each cell keeps
-its attributes and color pair, so the result can be written back
-unchanged with addstr().  At most n cells are read.
+Read from the current cursor position, or from y, x if specified,
+to the end of the line or at most n cells if n is specified.
+Unlike instr() and in_wstr(), each cell keeps its attributes and
+color pair, so the result can be written back unchanged with
+addstr().
 [clinic start generated code]*/
 
 static PyObject *
-_curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
-                              int y, int x, unsigned int n)
-/*[clinic end generated code: output=7fb5216f2088835b input=8104e661c3cb7fea]*/
+_curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_right_1,
+                              int y, int x, int group_right_2,
+                              unsigned int n)
+/*[clinic end generated code: output=3807a62d51efd44f input=50400321de1db1da]*/
 {
     int rtn;
-    unsigned int max_buf_size = 2048;
-
-    n = Py_MIN(n, max_buf_size - 1 - CURSES_STR_EXTRA);
-    n += CURSES_STR_EXTRA;
+    unsigned int limit = curses_window_read_limit(self, group_right_1, x, 1);
+    unsigned int nread = group_right_2 ? Py_MIN(n, limit) : limit;
+    unsigned int ask = nread + CURSES_STR_EXTRA;
     cursesmodule_state *state = get_cursesmodule_state_by_win(self);
     /* Zero the cells: reading a cell back through getcchar() relies on the
        cchar_t text array being NUL-terminated, which some curses libraries
        only guarantee for the characters they actually write. */
-    curses_cell_t *buf = PyMem_Calloc(n + 1, sizeof(curses_cell_t));
+    curses_cell_t *buf = PyMem_Calloc(ask + 1, sizeof(curses_cell_t));
     if (buf == NULL) {
         return PyErr_NoMemory();
     }
 
 #ifdef HAVE_NCURSESW
-    if (group_left_1) {
-        rtn = mvwin_wchnstr(self->win, y, x, buf, n);
+    if (group_right_1) {
+        rtn = mvwin_wchnstr(self->win, y, x, buf, ask);
     }
     else {
-        rtn = win_wchnstr(self->win, buf, n);
+        rtn = win_wchnstr(self->win, buf, ask);
     }
 
     if (rtn == ERR) {
@@ -4104,12 +4135,11 @@ _curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
         return PyCursesComplexStr_New(state, NULL, 0);
     }
 
-    n -= CURSES_STR_EXTRA;
-    /* win_wchnstr() stores at most n cells and zero-terminates the array at
-       the actual count; every real cell holds at least a space, so the first
+    /* win_wchnstr() stores at most nread cells and zero-terminates the array
+       at the actual count; every real cell holds at least a space, so the first
        empty cell marks the end of the run. */
     Py_ssize_t count = 0;
-    while (count < (Py_ssize_t)n) {
+    while (count < (Py_ssize_t)nread) {
         wchar_t wstr[CCHARW_MAX + 1];
         attr_t attrs;
         int pair;
@@ -4124,12 +4154,12 @@ _curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
     /* winchnstr() is not guaranteed (SVr4) to terminate the array, so pre-zero
        it and stop at the first empty cell; a painted cell always holds at least
        a space, never 0. */
-    memset(buf, 0, ((size_t)n + 1) * sizeof(curses_cell_t));
-    if (group_left_1) {
-        rtn = mvwinchnstr(self->win, y, x, buf, n);
+    memset(buf, 0, ((size_t)ask + 1) * sizeof(curses_cell_t));
+    if (group_right_1) {
+        rtn = mvwinchnstr(self->win, y, x, buf, ask);
     }
     else {
-        rtn = winchnstr(self->win, buf, n);
+        rtn = winchnstr(self->win, buf, ask);
     }
 
     if (rtn == ERR) {
@@ -4137,9 +4167,8 @@ _curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
         return PyCursesComplexStr_New(state, NULL, 0);
     }
 
-    n -= CURSES_STR_EXTRA;
     Py_ssize_t count = 0;
-    while (count < (Py_ssize_t)n && buf[count] != 0) {
+    while (count < (Py_ssize_t)nread && buf[count] != 0) {
         count++;
     }
 #endif
diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h
index 61c324e04c5bdc..44cb16d55fa330 100644
--- a/Modules/clinic/_cursesmodule.c.h
+++ b/Modules/clinic/_cursesmodule.c.h
@@ -1721,7 +1721,7 @@ _curses_window_inch(PyObject *self, PyObject *args)
 }
 
 PyDoc_STRVAR(_curses_window_instr__doc__,
-"instr([y, x,] n=2047)\n"
+"instr([y, x,] [n])\n"
 "Return the text of the window as a bytes object.\n"
 "\n"
 "  y\n"
@@ -1729,48 +1729,57 @@ PyDoc_STRVAR(_curses_window_instr__doc__,
 "  x\n"
 "    X-coordinate.\n"
 "  n\n"
-"    Maximal number of bytes.\n"
+"    Maximal number of bytes.  The rest of the line by default.\n"
 "\n"
 "Read from the current cursor position, or from y, x if specified, to\n"
-"the end of the line, and return the text in the encoding of the\n"
-"current locale, with attributes and color pairs stripped.  At most n\n"
-"bytes are read.");
+"the end of the line or at most n bytes if n is specified, and return\n"
+"the text in the encoding of the current locale, with attributes and\n"
+"color pairs stripped.");
 
 #define _CURSES_WINDOW_INSTR_METHODDEF    \
     {"instr", (PyCFunction)_curses_window_instr, METH_VARARGS, _curses_window_instr__doc__},
 
 static PyObject *
-_curses_window_instr_impl(PyCursesWindowObject *self, int group_left_1,
-                          int y, int x, unsigned int n);
+_curses_window_instr_impl(PyCursesWindowObject *self, int group_right_1,
+                          int y, int x, int group_right_2, unsigned int n);
 
 static PyObject *
 _curses_window_instr(PyObject *self, PyObject *args)
 {
     PyObject *return_value = NULL;
-    int group_left_1 = 0;
+    int group_right_1 = 0;
     int y = 0;
     int x = 0;
-    unsigned int n = 2047;
+    int group_right_2 = 0;
+    unsigned int n = 0;
 
     switch (PyTuple_GET_SIZE(args)) {
         case 0:
+            break;
         case 1:
-            if (!PyArg_ParseTuple(args, "|O&:instr", _PyLong_UnsignedInt_Converter, &n)) {
+            if (!PyArg_ParseTuple(args, "O&:instr", _PyLong_UnsignedInt_Converter, &n)) {
                 goto exit;
             }
+            group_right_2 = 1;
             break;
         case 2:
+            if (!PyArg_ParseTuple(args, "ii:instr", &y, &x)) {
+                goto exit;
+            }
+            group_right_1 = 1;
+            break;
         case 3:
-            if (!PyArg_ParseTuple(args, "ii|O&:instr", &y, &x, _PyLong_UnsignedInt_Converter, &n)) {
+            if (!PyArg_ParseTuple(args, "iiO&:instr", &y, &x, _PyLong_UnsignedInt_Converter, &n)) {
                 goto exit;
             }
-            group_left_1 = 1;
+            group_right_1 = 1;
+            group_right_2 = 1;
             break;
         default:
             PyErr_SetString(PyExc_TypeError, "_curses.window.instr requires 0 to 3 arguments");
             goto exit;
     }
-    return_value = _curses_window_instr_impl((PyCursesWindowObject *)self, group_left_1, y, x, n);
+    return_value = _curses_window_instr_impl((PyCursesWindowObject *)self, group_right_1, y, x, group_right_2, n);
 
 exit:
     return return_value;
@@ -1832,7 +1841,7 @@ _curses_window_get_wstr(PyObject *self, PyObject *args)
 }
 
 PyDoc_STRVAR(_curses_window_in_wstr__doc__,
-"in_wstr([y, x,] n=2047)\n"
+"in_wstr([y, x,] [n])\n"
 "Return the text of the window as a str.\n"
 "\n"
 "  y\n"
@@ -1840,55 +1849,64 @@ PyDoc_STRVAR(_curses_window_in_wstr__doc__,
 "  x\n"
 "    X-coordinate.\n"
 "  n\n"
-"    Maximal number of characters.\n"
+"    Maximal number of characters.  The rest of the line by default.\n"
 "\n"
 "This is the wide-character variant of instr().  Read from the\n"
 "current cursor position, or from y, x if specified, to the end of\n"
-"the line, with attributes and color pairs stripped.  At most n\n"
-"characters are read.");
+"the line or at most n characters if n is specified, with attributes\n"
+"and color pairs stripped.");
 
 #define _CURSES_WINDOW_IN_WSTR_METHODDEF    \
     {"in_wstr", (PyCFunction)_curses_window_in_wstr, METH_VARARGS, _curses_window_in_wstr__doc__},
 
 static PyObject *
-_curses_window_in_wstr_impl(PyCursesWindowObject *self, int group_left_1,
-                            int y, int x, unsigned int n);
+_curses_window_in_wstr_impl(PyCursesWindowObject *self, int group_right_1,
+                            int y, int x, int group_right_2, unsigned int n);
 
 static PyObject *
 _curses_window_in_wstr(PyObject *self, PyObject *args)
 {
     PyObject *return_value = NULL;
-    int group_left_1 = 0;
+    int group_right_1 = 0;
     int y = 0;
     int x = 0;
-    unsigned int n = 2047;
+    int group_right_2 = 0;
+    unsigned int n = 0;
 
     switch (PyTuple_GET_SIZE(args)) {
         case 0:
+            break;
         case 1:
-            if (!PyArg_ParseTuple(args, "|O&:in_wstr", _PyLong_UnsignedInt_Converter, &n)) {
+            if (!PyArg_ParseTuple(args, "O&:in_wstr", _PyLong_UnsignedInt_Converter, &n)) {
                 goto exit;
             }
+            group_right_2 = 1;
             break;
         case 2:
+            if (!PyArg_ParseTuple(args, "ii:in_wstr", &y, &x)) {
+                goto exit;
+            }
+            group_right_1 = 1;
+            break;
         case 3:
-            if (!PyArg_ParseTuple(args, "ii|O&:in_wstr", &y, &x, _PyLong_UnsignedInt_Converter, &n)) {
+            if (!PyArg_ParseTuple(args, "iiO&:in_wstr", &y, &x, _PyLong_UnsignedInt_Converter, &n)) {
                 goto exit;
             }
-            group_left_1 = 1;
+            group_right_1 = 1;
+            group_right_2 = 1;
             break;
         default:
             PyErr_SetString(PyExc_TypeError, "_curses.window.in_wstr requires 0 to 3 arguments");
             goto exit;
     }
-    return_value = _curses_window_in_wstr_impl((PyCursesWindowObject *)self, group_left_1, y, x, n);
+    return_value = _curses_window_in_wstr_impl((PyCursesWindowObject *)self, group_right_1, y, x, group_right_2, n);
 
 exit:
     return return_value;
 }
 
 PyDoc_STRVAR(_curses_window_in_wchstr__doc__,
-"in_wchstr([y, x,] n=2047)\n"
+"in_wchstr([y, x,] [n])\n"
 "Return the styled cells of the window as a complexstr.\n"
 "\n"
 "  y\n"
@@ -1896,48 +1914,59 @@ PyDoc_STRVAR(_curses_window_in_wchstr__doc__,
 "  x\n"
 "    X-coordinate.\n"
 "  n\n"
-"    Maximal number of cells.\n"
+"    Maximal number of cells.  The rest of the line by default.\n"
 "\n"
-"Read from the current cursor position, or from y, x if specified, to\n"
-"the end of the line.  Unlike instr() and in_wstr(), each cell keeps\n"
-"its attributes and color pair, so the result can be written back\n"
-"unchanged with addstr().  At most n cells are read.");
+"Read from the current cursor position, or from y, x if specified,\n"
+"to the end of the line or at most n cells if n is specified.\n"
+"Unlike instr() and in_wstr(), each cell keeps its attributes and\n"
+"color pair, so the result can be written back unchanged with\n"
+"addstr().");
 
 #define _CURSES_WINDOW_IN_WCHSTR_METHODDEF    \
     {"in_wchstr", (PyCFunction)_curses_window_in_wchstr, METH_VARARGS, _curses_window_in_wchstr__doc__},
 
 static PyObject *
-_curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_left_1,
-                              int y, int x, unsigned int n);
+_curses_window_in_wchstr_impl(PyCursesWindowObject *self, int group_right_1,
+                              int y, int x, int group_right_2,
+                              unsigned int n);
 
 static PyObject *
 _curses_window_in_wchstr(PyObject *self, PyObject *args)
 {
     PyObject *return_value = NULL;
-    int group_left_1 = 0;
+    int group_right_1 = 0;
     int y = 0;
     int x = 0;
-    unsigned int n = 2047;
+    int group_right_2 = 0;
+    unsigned int n = 0;
 
     switch (PyTuple_GET_SIZE(args)) {
         case 0:
+            break;
         case 1:
-            if (!PyArg_ParseTuple(args, "|O&:in_wchstr", _PyLong_UnsignedInt_Converter, &n)) {
+            if (!PyArg_ParseTuple(args, "O&:in_wchstr", _PyLong_UnsignedInt_Converter, &n)) {
                 goto exit;
             }
+            group_right_2 = 1;
             break;
         case 2:
+            if (!PyArg_ParseTuple(args, "ii:in_wchstr", &y, &x)) {
+                goto exit;
+            }
+            group_right_1 = 1;
+            break;
         case 3:
-            if (!PyArg_ParseTuple(args, "ii|O&:in_wchstr", &y, &x, _PyLong_UnsignedInt_Converter, &n)) {
+            if (!PyArg_ParseTuple(args, "iiO&:in_wchstr", &y, &x, _PyLong_UnsignedInt_Converter, &n)) {
                 goto exit;
             }
-            group_left_1 = 1;
+            group_right_1 = 1;
+            group_right_2 = 1;
             break;
         default:
             PyErr_SetString(PyExc_TypeError, "_curses.window.in_wchstr requires 0 to 3 arguments");
             goto exit;
     }
-    return_value = _curses_window_in_wchstr_impl((PyCursesWindowObject *)self, group_left_1, y, x, n);
+    return_value = _curses_window_in_wchstr_impl((PyCursesWindowObject *)self, group_right_1, y, x, group_right_2, n);
 
 exit:
     return return_value;
@@ -6603,4 +6632,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored
 #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
     #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
 #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */
-/*[clinic end generated code: output=5616d0371c2240be input=a9049054013a1b77]*/
+/*[clinic end generated code: output=81cb3f7a7225f920 input=a9049054013a1b77]*/

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.