From: Serhiy Storchaka Date: Fri, 19 Jun 2026 17:49:26 +0000 (+0300) Subject: gh-151744: Add curses.nofilter() (GH-151747) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2e5843e13fcfd768a435d82e6182af403844432c;p=thirdparty%2FPython%2Fcpython.git 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) --- diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 5726aee5af89..d7873054d6b9 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 8e4c4a1e9b1d..0a110795f371 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 c6a762c04e05..98f1a7c8a0a2 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -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 index 000000000000..24328a8f9f93 --- /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 02a8e2c1b1bc..e60cba3ef87e 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 cab9b068a561..f577368680ef 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 d73a88d04016..12fd0d15698a 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 +#elif defined(HAVE_NCURSESW_CURSES_H) +# include +#elif defined(HAVE_NCURSES_NCURSES_H) +# include +#elif defined(HAVE_NCURSES_CURSES_H) +# include +#elif defined(HAVE_NCURSES_H) +# include +#elif defined(HAVE_CURSES_H) +# include +#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 8ba134c77b34..3b42fdfe4038 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 999a55a5efd0..2bef8d38497c 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