https://github.com/python/cpython/commit/23180c50082fe98784c78511b335d7274ed87fb7
commit: 23180c50082fe98784c78511b335d7274ed87fb7
branch: main
author: Vyron Vasileiadis <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-09-07T22:44:20+03:00
summary:

gh-156953: Fix a reference leak in curses window.insnstr() (GH-156954)

insnstr() did not release the bytes object it converted its argument to
when setting the attributes failed, unlike addstr(), addnstr() and
insstr().

files:
M Lib/test/test_curses.py
M Modules/_cursesmodule.c

diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index 0c63b04a461453..bbcf8d285a6774 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -3386,6 +3386,23 @@ def test_close(self):
         # close() is idempotent.
         screen.close()
 
+    def test_close_then_write_with_attr_keeps_no_reference(self):
+        # A write with an *attr* argument on a detached window fails while
+        # setting the rendition, and has to release the bytes it converted.
+        s = self.make_pty()
+        screen = curses.newterm('xterm', s, s)
+        win = screen.stdscr
+        screen.close()
+        writes = [lambda b: win.addstr(b, curses.A_BOLD),
+                  lambda b: win.addnstr(b, 4, curses.A_BOLD),
+                  lambda b: win.insstr(b, curses.A_BOLD),
+                  lambda b: win.insnstr(b, 4, curses.A_BOLD)]
+        data = b'x' * 8
+        nrefs = sys.getrefcount(data)
+        for write in writes:
+            self.assertRaises(curses.error, write, data)
+        self.assertEqual(sys.getrefcount(data), nrefs)
+
     @requires_curses_func('panel')
     def test_close_then_panel_replace(self):
         # A detached window has no underlying curses window, so replace()
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index f22aac624a8a66..24cfbcdd503cee 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -4364,6 +4364,7 @@ _curses_window_insnstr_impl(PyCursesWindowObject *self, 
int group_left_1,
             curses_wattrset(self, attr, "insnstr") < 0)
         {
             curses_release_wstr(strtype, wstr);
+            Py_XDECREF(bytesobj);
             return NULL;
         }
     }

_______________________________________________
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