Issue #12567: Add curses.unget_wch() function
Push a character so the next get_wch() will return it.
diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index f27b2ff..df25910 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -598,6 +598,17 @@
Only one *ch* can be pushed before :meth:`getch` is called.
+.. function:: unget_wch(ch)
+
+ Push *ch* so the next :meth:`get_wch` will return it.
+
+ .. note::
+
+ Only one *ch* can be pushed before :meth:`get_wch` is called.
+
+ .. versionadded:: 3.3
+
+
.. function:: ungetmouse(id, x, y, z, bstate)
Push a :const:`KEY_MOUSE` event onto the input queue, associating the given
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index c767e93..8caf0de 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -264,6 +264,20 @@
curses.ungetch(1025)
stdscr.getkey()
+def test_unget_wch(stdscr):
+ ch = '\xe9'
+ curses.unget_wch(ch)
+ read = stdscr.get_wch()
+ read = chr(read)
+ if read != ch:
+ raise AssertionError("%r != %r" % (read, ch))
+
+ ch = ord('\xe9')
+ curses.unget_wch(ch)
+ read = stdscr.get_wch()
+ if read != ch:
+ raise AssertionError("%r != %r" % (read, ch))
+
def main(stdscr):
curses.savetty()
try:
@@ -272,6 +286,7 @@
test_userptr_without_set(stdscr)
test_resize_term(stdscr)
test_issue6243(stdscr)
+ test_unget_wch(stdscr)
finally:
curses.resetty()
diff --git a/Misc/NEWS b/Misc/NEWS
index fbcdb8d..85af2ac 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -271,6 +271,9 @@
Library
-------
+- Issue #12567: Add curses.unget_wch() function. Push a character so the next
+ get_wch() will return it.
+
- Issue #9561: distutils and packaging now writes egg-info files using UTF-8,
instead of the locale encoding.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 6d72024..ef0a66c 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -2696,6 +2696,71 @@
return PyCursesCheckERR(ungetch(ch), "ungetch");
}
+#ifdef HAVE_NCURSESW
+/* Convert an object to a character (wchar_t):
+
+ - int
+ - str of length 1
+
+ Return 1 on success, 0 on error. */
+static int
+PyCurses_ConvertToWchar_t(PyObject *obj,
+ wchar_t *wch)
+{
+ if (PyUnicode_Check(obj)) {
+ wchar_t buffer[2];
+ if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) {
+ PyErr_Format(PyExc_TypeError,
+ "expect bytes or str of length 1, or int, "
+ "got a str of length %zi",
+ PyUnicode_GET_SIZE(obj));
+ return 0;
+ }
+ *wch = buffer[0];
+ return 2;
+ }
+ else if (PyLong_CheckExact(obj)) {
+ long value;
+ int overflow;
+ value = PyLong_AsLongAndOverflow(obj, &overflow);
+ if (overflow) {
+ PyErr_SetString(PyExc_OverflowError,
+ "int doesn't fit in long");
+ return 0;
+ }
+ *wch = (wchar_t)value;
+ if ((long)*wch != value) {
+ PyErr_Format(PyExc_OverflowError,
+ "character doesn't fit in wchar_t");
+ return 0;
+ }
+ return 1;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "expect bytes or str of length 1, or int, got %s",
+ Py_TYPE(obj)->tp_name);
+ return 0;
+ }
+}
+
+static PyObject *
+PyCurses_Unget_Wch(PyObject *self, PyObject *args)
+{
+ PyObject *obj;
+ wchar_t wch;
+
+ PyCursesInitialised;
+
+ if (!PyArg_ParseTuple(args,"O", &obj))
+ return NULL;
+
+ if (!PyCurses_ConvertToWchar_t(obj, &wch))
+ return NULL;
+ return PyCursesCheckERR(unget_wch(wch), "unget_wch");
+}
+#endif
+
static PyObject *
PyCurses_Use_Env(PyObject *self, PyObject *args)
{
@@ -2823,6 +2888,9 @@
{"typeahead", (PyCFunction)PyCurses_TypeAhead, METH_VARARGS},
{"unctrl", (PyCFunction)PyCurses_UnCtrl, METH_VARARGS},
{"ungetch", (PyCFunction)PyCurses_UngetCh, METH_VARARGS},
+#ifdef HAVE_NCURSESW
+ {"unget_wch", (PyCFunction)PyCurses_Unget_Wch, METH_VARARGS},
+#endif
{"use_env", (PyCFunction)PyCurses_Use_Env, METH_VARARGS},
#ifndef STRICT_SYSV_CURSES
{"use_default_colors", (PyCFunction)PyCurses_Use_Default_Colors, METH_NOARGS},