]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-151744: Add curses.nofilter() (GH-151747) main
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 19 Jun 2026 17:49:26 +0000 (20:49 +0300)
committerGitHub <noreply@github.com>
Fri, 19 Jun 2026 17:49:26 +0000 (17:49 +0000)
Wrap the ncurses nofilter() function, which undoes the effect of
filter().  Without it there is no way to restore normal screen sizing
after a curses.filter() call in the same process.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Doc/library/curses.rst
Doc/whatsnew/3.16.rst
Lib/test/test_curses.py
Misc/NEWS.d/next/Library/2026-06-19-18-40-00.gh-issue-151744.Kp7mNq.rst [new file with mode: 0644]
Modules/_cursesmodule.c
Modules/clinic/_cursesmodule.c.h
configure
configure.ac
pyconfig.h.in

index 5726aee5af89b129d56d27d93d1e7f72c39a47cf..d7873054d6b9154073a9a6fd95e29c3edb2be66e 100644 (file)
@@ -204,6 +204,17 @@ The module :mod:`!curses` defines the following functions:
    character-at-a-time  line editing without touching the rest of the screen.
 
 
+.. function:: nofilter()
+
+   Undo the effect of a previous :func:`.filter` call.
+   Like :func:`.filter`, it must be called before :func:`initscr` so that the
+   next initialization uses the full screen again.
+
+   Availability: if the underlying curses library provides ``nofilter()``.
+
+   .. versionadded:: next
+
+
 .. function:: flash()
 
    Flash the screen.  That is, change it to reverse-video and then change it back
index 8e4c4a1e9b1de028b89369224265d7df9270958f..0a110795f371eb7f1279c46644564881036e7c35 100644 (file)
@@ -86,6 +86,12 @@ New modules
 Improved modules
 ================
 
+curses
+------
+
+* Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
+  (Contributed by Serhiy Storchaka in :gh:`151744`.)
+
 gzip
 ----
 
index c6a762c04e05253f39f5b1c2c8df9fedbf1f7440..98f1a7c8a0a2c5c060692fbedd446c51a0c53233 100644 (file)
@@ -120,8 +120,9 @@ class TestCurses(unittest.TestCase):
     @requires_curses_func('filter')
     def test_filter(self):
         # TODO: Should be called before initscr() or newterm() are called.
-        # TODO: nofilter()
         curses.filter()
+        if hasattr(curses, 'nofilter'):
+            curses.nofilter()
 
     @requires_curses_func('use_env')
     def test_use_env(self):
diff --git a/Misc/NEWS.d/next/Library/2026-06-19-18-40-00.gh-issue-151744.Kp7mNq.rst b/Misc/NEWS.d/next/Library/2026-06-19-18-40-00.gh-issue-151744.Kp7mNq.rst
new file mode 100644 (file)
index 0000000..24328a8
--- /dev/null
@@ -0,0 +1 @@
+Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
index 02a8e2c1b1bc1053d035bf79575bf2c1f510e4b1..e60cba3ef87ead1c353bf70bb8bd3dbe8fe30333 100644 (file)
@@ -3220,6 +3220,28 @@ _curses_filter_impl(PyObject *module)
 }
 #endif
 
+#ifdef HAVE_CURSES_NOFILTER
+/*[clinic input]
+_curses.nofilter
+
+Undo the effect of a preceding filter() call.
+
+Must be called before initscr().  It restores the normal behaviour
+disabled by filter(), so that the next initscr() uses the full screen
+rather than a single line.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_nofilter_impl(PyObject *module)
+/*[clinic end generated code: output=d95ca4d48a6bdbdf input=58aea83b1a5c969f]*/
+{
+    /* not checking for PyCursesInitialised here since nofilter() must
+       be called before initscr() */
+    nofilter();
+    Py_RETURN_NONE;
+}
+#endif
+
 /*[clinic input]
 _curses.baudrate
 
@@ -5321,6 +5343,7 @@ static PyMethodDef cursesmodule_methods[] = {
     _CURSES_ENDWIN_METHODDEF
     _CURSES_ERASECHAR_METHODDEF
     _CURSES_FILTER_METHODDEF
+    _CURSES_NOFILTER_METHODDEF
     _CURSES_FLASH_METHODDEF
     _CURSES_FLUSHINP_METHODDEF
     _CURSES_GETMOUSE_METHODDEF
index cab9b068a561da4c5d22103d371cf050b76723cc..f577368680ef572ed26afc13c32a5c4619d60d22 100644 (file)
@@ -1866,6 +1866,32 @@ _curses_filter(PyObject *module, PyObject *Py_UNUSED(ignored))
 
 #endif /* defined(HAVE_CURSES_FILTER) */
 
+#if defined(HAVE_CURSES_NOFILTER)
+
+PyDoc_STRVAR(_curses_nofilter__doc__,
+"nofilter($module, /)\n"
+"--\n"
+"\n"
+"Undo the effect of a preceding filter() call.\n"
+"\n"
+"Must be called before initscr().  It restores the normal behaviour\n"
+"disabled by filter(), so that the next initscr() uses the full screen\n"
+"rather than a single line.");
+
+#define _CURSES_NOFILTER_METHODDEF    \
+    {"nofilter", (PyCFunction)_curses_nofilter, METH_NOARGS, _curses_nofilter__doc__},
+
+static PyObject *
+_curses_nofilter_impl(PyObject *module);
+
+static PyObject *
+_curses_nofilter(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_nofilter_impl(module);
+}
+
+#endif /* defined(HAVE_CURSES_NOFILTER) */
+
 PyDoc_STRVAR(_curses_baudrate__doc__,
 "baudrate($module, /)\n"
 "--\n"
@@ -4407,6 +4433,10 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored
     #define _CURSES_FILTER_METHODDEF
 #endif /* !defined(_CURSES_FILTER_METHODDEF) */
 
+#ifndef _CURSES_NOFILTER_METHODDEF
+    #define _CURSES_NOFILTER_METHODDEF
+#endif /* !defined(_CURSES_NOFILTER_METHODDEF) */
+
 #ifndef _CURSES_GETSYX_METHODDEF
     #define _CURSES_GETSYX_METHODDEF
 #endif /* !defined(_CURSES_GETSYX_METHODDEF) */
@@ -4486,4 +4516,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=11ab7c93cbc13e75 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=7494804bf2c4d1f5 input=a9049054013a1b77]*/
index d73a88d04016ddb43329788b802d3f2d90e98132..12fd0d15698ac1d471cd626f6d12d81eaf19db75 100755 (executable)
--- a/configure
+++ b/configure
 
 
 
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for curses function nofilter" >&5
+printf %s "checking for curses function nofilter... " >&6; }
+if test ${ac_cv_lib_curses_nofilter+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#define NCURSES_OPAQUE 0
+#if defined(HAVE_NCURSESW_NCURSES_H)
+#  include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSESW_CURSES_H)
+#  include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#  include <ncurses/ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+#  include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+#  include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+#  include <curses.h>
+#endif
+
+int
+main (void)
+{
+
+        #ifndef nofilter
+        void *x=nofilter
+        #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_lib_curses_nofilter=yes
+else case e in #(
+  e) ac_cv_lib_curses_nofilter=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+   ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_nofilter" >&5
+printf "%s\n" "$ac_cv_lib_curses_nofilter" >&6; }
+  if test "x$ac_cv_lib_curses_nofilter" = xyes
+then :
+
+printf "%s\n" "#define HAVE_CURSES_NOFILTER 1" >>confdefs.h
+
+fi
+
+
+
+
+
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for curses function has_key" >&5
 printf %s "checking for curses function has_key... " >&6; }
 if test ${ac_cv_lib_curses_has_key+y}
index 8ba134c77b341299ef611c25e165a1001eeaa99b..3b42fdfe40385dcadb7cb15730174be572fed2f3 100644 (file)
@@ -7192,6 +7192,7 @@ PY_CHECK_CURSES_FUNC([immedok])
 PY_CHECK_CURSES_FUNC([syncok])
 PY_CHECK_CURSES_FUNC([wchgat])
 PY_CHECK_CURSES_FUNC([filter])
+PY_CHECK_CURSES_FUNC([nofilter])
 PY_CHECK_CURSES_FUNC([has_key])
 PY_CHECK_CURSES_FUNC([typeahead])
 PY_CHECK_CURSES_FUNC([use_env])
index 999a55a5efd0fb0467b9c43966040c245f110f70..2bef8d38497c547cacd03b473adaa002a1b9e6d5 100644 (file)
 /* Define if you have the 'is_term_resized' function. */
 #undef HAVE_CURSES_IS_TERM_RESIZED
 
+/* Define if you have the 'nofilter' function. */
+#undef HAVE_CURSES_NOFILTER
+
 /* Define if you have the 'resizeterm' function. */
 #undef HAVE_CURSES_RESIZETERM