From bca2b4cfeb3a98b150099a9d4ad488ba1b982ae0 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Wed, 27 May 2026 22:27:07 +0200 Subject: [PATCH 1/2] fix: eliminate memory leak in readline.c --- Modules/readline.c | 56 ++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 2cc3d40baa3aba1..11f14828de3466b 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1357,29 +1357,41 @@ setup_readline(readlinestate *mod_state) if (using_libedit_emulation) rl_initialize(); - /* Detect if libedit's readline emulation uses 0-based - * indexing or 1-based indexing. - */ - add_history("1"); - if (history_get(1) == NULL) { - libedit_history_start = 0; - } else { - libedit_history_start = 1; - } - /* Some libedit implementations use 1 based indexing on - * replace_history_entry where libreadline uses 0 based. - * The API our module presents is supposed to be 0 based. - * It's a mad mad mad mad world. - */ - { - add_history("2"); - HIST_ENTRY *old_entry = replace_history_entry(1, "X", NULL); - _py_free_history_entry_lock_held(old_entry); - HIST_ENTRY *item = history_get(libedit_history_start); - if (item && item->line && strcmp(item->line, "X")) { - libedit_append_replace_history_offset = 0; + if (using_libedit_emulation) { + /* Detect if libedit's readline emulation uses 0-based + * indexing or 1-based indexing. + */ + add_history("1"); + if (history_get(1) == NULL) { + libedit_history_start = 0; } else { - libedit_append_replace_history_offset = 1; + libedit_history_start = 1; + } + /* Some libedit implementations use 1 based indexing on + * replace_history_entry where libreadline uses 0 based. + * The API our module presents is supposed to be 0 based. + * It's a mad mad mad mad world. + */ + { + add_history("2"); + HIST_ENTRY *old_entry = replace_history_entry(1, "X", NULL); + _py_free_history_entry_lock_held(old_entry); + HIST_ENTRY *item = history_get(libedit_history_start); + if (item && item->line && strcmp(item->line, "X")) { + libedit_append_replace_history_offset = 0; + } else { + libedit_append_replace_history_offset = 1; + } + } + /* Use remove_history instead of clear_history to explicitly free + * each probe entry: some libedit builds do not free the strdup'd line + * strings inside clear_history, causing a small leak per process. */ + while (history_length > 0) { + HIST_ENTRY *entry = remove_history(libedit_history_start); + if (entry == NULL) { + break; + } + _py_free_history_entry_lock_held(entry); } } clear_history(); From e1fafd06fa0958556530424c501466e6c897d498 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Wed, 27 May 2026 22:33:49 +0200 Subject: [PATCH 2/2] misc: add news entry --- .../Library/2026-05-27-22-33-25.gh-issue-150536.j0KkObbz.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-05-27-22-33-25.gh-issue-150536.j0KkObbz.rst diff --git a/Misc/NEWS.d/next/Library/2026-05-27-22-33-25.gh-issue-150536.j0KkObbz.rst b/Misc/NEWS.d/next/Library/2026-05-27-22-33-25.gh-issue-150536.j0KkObbz.rst new file mode 100644 index 000000000000000..041ccc2f5fe69de --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-27-22-33-25.gh-issue-150536.j0KkObbz.rst @@ -0,0 +1 @@ +Fix a memory leak in the :mod:`readline` module on macOS.