https://github.com/python/cpython/commit/2e5843e13fcfd768a435d82e6182af403844432c
commit: 2e5843e13fcfd768a435d82e6182af403844432c
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-19T17:49:26Z
summary:

gh-151744: Add curses.nofilter() (GH-151747)

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) <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-19-18-40-00.gh-issue-151744.Kp7mNq.rst
M Doc/library/curses.rst
M Doc/whatsnew/3.16.rst
M Lib/test/test_curses.py
M Modules/_cursesmodule.c
M Modules/clinic/_cursesmodule.c.h
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index 5726aee5af89b1..d7873054d6b915 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -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
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 8e4c4a1e9b1de0..0a110795f371eb 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -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
 ----
 
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index c6a762c04e0525..98f1a7c8a0a2c5 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -120,8 +120,9 @@ def setUp(self):
     @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
index 00000000000000..24328a8f9f9342
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-19-18-40-00.gh-issue-151744.Kp7mNq.rst
@@ -0,0 +1 @@
+Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 02a8e2c1b1bc10..e60cba3ef87ead 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -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
diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h
index cab9b068a561da..f577368680ef57 100644
--- a/Modules/clinic/_cursesmodule.c.h
+++ b/Modules/clinic/_cursesmodule.c.h
@@ -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]*/
diff --git a/configure b/configure
index d73a88d04016dd..12fd0d15698ac1 100755
--- a/configure
+++ b/configure
@@ -30286,6 +30286,66 @@ fi
 
 
 
+  { 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}
diff --git a/configure.ac b/configure.ac
index 8ba134c77b3412..3b42fdfe40385d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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])
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 999a55a5efd0fb..2bef8d38497c54 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -218,6 +218,9 @@
 /* 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
 

_______________________________________________
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]

Reply via email to