From e8d211582670ead9e7420998fb1d7f93c7a8dd16 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Wed, 22 Nov 2023 19:39:22 +1100 Subject: [PATCH 01/16] Catch import error conditions with readline callback hook on_startup_hook --- Modules/readline.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index fde552d124bc77..9b1a2673e882aa 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1026,7 +1026,19 @@ on_startup_hook(void) { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - r = on_hook(readlinestate_global->startup_hook); + PyObject* mod = PyState_FindModule(&readlinemodule); + if (mod == NULL) { + PyGILState_Release(gilstate); + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); + } + return -1; + } else { + Py_INCREF(mod); + } + + r = on_hook(get_readline_state(mod)->startup_hook); + Py_DECREF(mod); PyGILState_Release(gilstate); return r; } @@ -1511,12 +1523,17 @@ PyInit_readline(void) } mod_state = (readlinestate *) PyModule_GetState(m); + if (mod_state == NULL){ + goto error; + } PyOS_ReadlineFunctionPointer = call_readline; if (setup_readline(mod_state) < 0) { PyErr_NoMemory(); goto error; } - + if (PyErr_Occurred()){ + goto error; + } return m; error: From d56fb47691f6bc5dac18246967c4343f53d86bbd Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Wed, 22 Nov 2023 19:44:03 +1100 Subject: [PATCH 02/16] Add news entry --- .../next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst diff --git a/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst b/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst new file mode 100644 index 00000000000000..8345e33791cde0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-22-19-43-54.gh-issue-112292.5nDU87.rst @@ -0,0 +1,2 @@ +Fix a crash in :mod:`readline` when imported from a sub interpreter. Patch +by Anthony Shaw From 6e9e66d49d7516e0bfaac2762ed8d94d13248de0 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 23 Nov 2023 10:29:39 +1100 Subject: [PATCH 03/16] Move the GIL release to after setting an error Co-authored-by: Eric Snow --- Modules/readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/readline.c b/Modules/readline.c index 9b1a2673e882aa..f8d07b095cdc96 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1028,10 +1028,10 @@ on_startup_hook(void) PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject* mod = PyState_FindModule(&readlinemodule); if (mod == NULL) { - PyGILState_Release(gilstate); if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); } + PyGILState_Release(gilstate); return -1; } else { Py_INCREF(mod); From 364d8d5dd7dfe12bed047f02590649e384649b8a Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 23 Nov 2023 10:30:55 +1100 Subject: [PATCH 04/16] remove else --- Modules/readline.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 9b1a2673e882aa..218747621bb1e8 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1033,10 +1033,8 @@ on_startup_hook(void) PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); } return -1; - } else { - Py_INCREF(mod); } - + Py_INCREF(mod); r = on_hook(get_readline_state(mod)->startup_hook); Py_DECREF(mod); PyGILState_Release(gilstate); From 8890f5cfcbd9b350792ea2f62dced1f2bb9fdd6b Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 23 Nov 2023 10:49:04 +1100 Subject: [PATCH 05/16] Harden the on_pre_input_hook --- Modules/readline.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Modules/readline.c b/Modules/readline.c index 05f5e1ebaba9be..6db8de2e986ac3 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -126,6 +126,16 @@ readline_clear(PyObject *m) return 0; } +static void +readline_cleanup(void) +{ + rl_startup_hook = NULL; +#ifdef HAVE_RL_PRE_INPUT_HOOK + rl_pre_input_hook = NULL; +#endif + rl_attempted_completion_function = NULL; +} + static int readline_traverse(PyObject *m, visitproc visit, void *arg) { @@ -1031,6 +1041,7 @@ on_startup_hook(void) if (!PyErr_Occurred()) { PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); } + readline_cleanup(); PyGILState_Release(gilstate); return -1; } @@ -1053,7 +1064,18 @@ on_pre_input_hook(void) { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - r = on_hook(readlinestate_global->pre_input_hook); + PyObject* mod = PyState_FindModule(&readlinemodule); + if (mod == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); + } + readline_cleanup(); + PyGILState_Release(gilstate); + return -1; + } + Py_INCREF(mod); + r = on_hook(get_readline_state(mod)->pre_input_hook); + Py_DECREF(mod); PyGILState_Release(gilstate); return r; } From f4580c8783818f2ae314c4770f97f1a10f802e53 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 23 Nov 2023 11:23:58 +1100 Subject: [PATCH 06/16] Replace uses of a global macro with no error checking with a function to get global state --- Modules/readline.c | 86 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 6db8de2e986ac3..637595dea3c1bc 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -157,8 +157,19 @@ readline_free(void *m) static PyModuleDef readlinemodule; -#define readlinestate_global ((readlinestate *)PyModule_GetState(PyState_FindModule(&readlinemodule))) - +static inline readlinestate* +get_global_readline_state(void) +{ + PyObject *mod = PyState_FindModule(&readlinemodule); + if (mod == NULL){ + if (!PyErr_Occurred()){ + PyErr_SetString(PyExc_RuntimeError, + "readline module state not initialized"); + } + return NULL; + } + return get_readline_state(mod); +} /* Convert to/from multibyte C strings */ @@ -448,14 +459,18 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/ { + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } PyObject *result = set_hook("completion_display_matches_hook", - &readlinestate_global->completion_display_matches_hook, + &state->completion_display_matches_hook, function); #ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK /* We cannot set this hook globally, since it replaces the default completion display. */ rl_completion_display_matches_hook = - readlinestate_global->completion_display_matches_hook ? + state->completion_display_matches_hook ? #if defined(HAVE_RL_COMPDISP_FUNC_T) (rl_compdisp_func_t *)on_completion_display_matches_hook : 0; #else @@ -482,7 +497,11 @@ static PyObject * readline_set_startup_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=02cd0e0c4fa082ad input=7783b4334b26d16d]*/ { - return set_hook("startup_hook", &readlinestate_global->startup_hook, + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } + return set_hook("startup_hook", &state->startup_hook, function); } @@ -507,7 +526,11 @@ static PyObject * readline_set_pre_input_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=fe1a96505096f464 input=4f3eaeaf7ce1fdbe]*/ { - return set_hook("pre_input_hook", &readlinestate_global->pre_input_hook, + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } + return set_hook("pre_input_hook", &state->pre_input_hook, function); } #endif @@ -540,7 +563,11 @@ static PyObject * readline_get_begidx_impl(PyObject *module) /*[clinic end generated code: output=362616ee8ed1b2b1 input=e083b81c8eb4bac3]*/ { - return Py_NewRef(readlinestate_global->begidx); + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } + return Py_NewRef(state->begidx); } /* Get the ending index for the scope of the tab-completion */ @@ -555,7 +582,11 @@ static PyObject * readline_get_endidx_impl(PyObject *module) /*[clinic end generated code: output=7f763350b12d7517 input=d4c7e34a625fd770]*/ { - return Py_NewRef(readlinestate_global->endidx); + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } + return Py_NewRef(state->endidx); } /* Set the tab-completion word-delimiters that readline uses */ @@ -782,7 +813,11 @@ static PyObject * readline_set_completer_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=171a2a60f81d3204 input=51e81e13118eb877]*/ { - return set_hook("completer", &readlinestate_global->completer, function); + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } + return set_hook("completer", &state->completer, function); } /*[clinic input] @@ -795,10 +830,14 @@ static PyObject * readline_get_completer_impl(PyObject *module) /*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/ { - if (readlinestate_global->completer == NULL) { + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return NULL; + } + if (state->completer == NULL) { Py_RETURN_NONE; } - return Py_NewRef(readlinestate_global->completer); + return Py_NewRef(state->completer); } /* Private function to get current length of history. XXX It may be @@ -1091,6 +1130,10 @@ on_completion_display_matches_hook(char **matches, { int i; PyObject *sub, *m=NULL, *s=NULL, *r=NULL; + readlinestate *state = get_global_readline_state(); + if (state == NULL) { + return; + } PyGILState_STATE gilstate = PyGILState_Ensure(); m = PyList_New(num_matches); if (m == NULL) @@ -1102,7 +1145,7 @@ on_completion_display_matches_hook(char **matches, PyList_SET_ITEM(m, i, s); } sub = decode(matches[0]); - r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook, + r = PyObject_CallFunction(state->completion_display_matches_hook, "NNi", sub, m, max_length); m=NULL; @@ -1150,12 +1193,16 @@ static char * on_completion(const char *text, int state) { char *result = NULL; - if (readlinestate_global->completer != NULL) { + readlinestate *module_state = get_global_readline_state(); + if (module_state == NULL) { + return NULL; + } + if (module_state->completer != NULL) { PyObject *r = NULL, *t; PyGILState_STATE gilstate = PyGILState_Ensure(); rl_attempted_completion_over = 1; t = decode(text); - r = PyObject_CallFunction(readlinestate_global->completer, "Ni", t, state); + r = PyObject_CallFunction(module_state->completer, "Ni", t, state); if (r == NULL) goto error; if (r == Py_None) { @@ -1191,6 +1238,7 @@ flex_complete(const char *text, int start, int end) char saved; size_t start_size, end_size; wchar_t *s; + readlinestate *state = get_global_readline_state(); PyGILState_STATE gilstate = PyGILState_Ensure(); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character ='\0'; @@ -1219,10 +1267,12 @@ flex_complete(const char *text, int start, int end) end = start + (int)end_size; done: - Py_XDECREF(readlinestate_global->begidx); - Py_XDECREF(readlinestate_global->endidx); - readlinestate_global->begidx = PyLong_FromLong((long) start); - readlinestate_global->endidx = PyLong_FromLong((long) end); + if (state) { + Py_XDECREF(state->begidx); + Py_XDECREF(state->endidx); + state->begidx = PyLong_FromLong((long) start); + state->endidx = PyLong_FromLong((long) end); + } result = completion_matches((char *)text, *on_completion); PyGILState_Release(gilstate); return result; From b408004fa01cc8c601d6140909ee328a132bd6f6 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 23 Nov 2023 11:33:18 +1100 Subject: [PATCH 07/16] Don't set exceptions on non-Python calls. --- Modules/readline.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 637595dea3c1bc..5c32255f30f8f9 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -158,11 +158,11 @@ readline_free(void *m) static PyModuleDef readlinemodule; static inline readlinestate* -get_global_readline_state(void) +get_global_readline_state(bool set_error) { PyObject *mod = PyState_FindModule(&readlinemodule); if (mod == NULL){ - if (!PyErr_Occurred()){ + if (set_error && !PyErr_Occurred()){ PyErr_SetString(PyExc_RuntimeError, "readline module state not initialized"); } @@ -171,6 +171,8 @@ get_global_readline_state(void) return get_readline_state(mod); } +#define get_global_readline_state_with_error() get_global_readline_state(true) + /* Convert to/from multibyte C strings */ static PyObject * @@ -459,7 +461,7 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -497,7 +499,7 @@ static PyObject * readline_set_startup_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=02cd0e0c4fa082ad input=7783b4334b26d16d]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -526,7 +528,7 @@ static PyObject * readline_set_pre_input_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=fe1a96505096f464 input=4f3eaeaf7ce1fdbe]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -563,7 +565,7 @@ static PyObject * readline_get_begidx_impl(PyObject *module) /*[clinic end generated code: output=362616ee8ed1b2b1 input=e083b81c8eb4bac3]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -582,7 +584,7 @@ static PyObject * readline_get_endidx_impl(PyObject *module) /*[clinic end generated code: output=7f763350b12d7517 input=d4c7e34a625fd770]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -813,7 +815,7 @@ static PyObject * readline_set_completer_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=171a2a60f81d3204 input=51e81e13118eb877]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -830,7 +832,7 @@ static PyObject * readline_get_completer_impl(PyObject *module) /*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/ { - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state_with_error(); if (state == NULL) { return NULL; } @@ -1130,7 +1132,7 @@ on_completion_display_matches_hook(char **matches, { int i; PyObject *sub, *m=NULL, *s=NULL, *r=NULL; - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state(false); if (state == NULL) { return; } @@ -1193,7 +1195,7 @@ static char * on_completion(const char *text, int state) { char *result = NULL; - readlinestate *module_state = get_global_readline_state(); + readlinestate *module_state = get_global_readline_state(false); if (module_state == NULL) { return NULL; } @@ -1238,7 +1240,7 @@ flex_complete(const char *text, int start, int end) char saved; size_t start_size, end_size; wchar_t *s; - readlinestate *state = get_global_readline_state(); + readlinestate *state = get_global_readline_state(false); PyGILState_STATE gilstate = PyGILState_Ensure(); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character ='\0'; From 30a1a734fb52d708149707650956fb13decfe974 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Thu, 23 Nov 2023 13:10:31 +1100 Subject: [PATCH 08/16] Acquire GIL before checking module state since it will require tstate checks (won't raise assertion in non-debug code) --- Modules/readline.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 5c32255f30f8f9..1d0b009d548b82 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1132,11 +1132,12 @@ on_completion_display_matches_hook(char **matches, { int i; PyObject *sub, *m=NULL, *s=NULL, *r=NULL; + PyGILState_STATE gilstate = PyGILState_Ensure(); readlinestate *state = get_global_readline_state(false); if (state == NULL) { + PyGILState_Release(gilstate); return; } - PyGILState_STATE gilstate = PyGILState_Ensure(); m = PyList_New(num_matches); if (m == NULL) goto error; @@ -1195,13 +1196,14 @@ static char * on_completion(const char *text, int state) { char *result = NULL; + PyGILState_STATE gilstate = PyGILState_Ensure(); readlinestate *module_state = get_global_readline_state(false); if (module_state == NULL) { + PyGILState_Release(gilstate); return NULL; } if (module_state->completer != NULL) { PyObject *r = NULL, *t; - PyGILState_STATE gilstate = PyGILState_Ensure(); rl_attempted_completion_over = 1; t = decode(text); r = PyObject_CallFunction(module_state->completer, "Ni", t, state); @@ -1226,6 +1228,7 @@ on_completion(const char *text, int state) PyGILState_Release(gilstate); return result; } + PyGILState_Release(gilstate); return result; } @@ -1240,8 +1243,8 @@ flex_complete(const char *text, int start, int end) char saved; size_t start_size, end_size; wchar_t *s; - readlinestate *state = get_global_readline_state(false); PyGILState_STATE gilstate = PyGILState_Ensure(); + readlinestate *state = get_global_readline_state(false); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character ='\0'; #endif From 451513bb588fdbda497cbcd7afe075f8a2fd9865 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 10:04:57 +1100 Subject: [PATCH 09/16] Update Modules/readline.c Co-authored-by: Eric Snow --- Modules/readline.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/readline.c b/Modules/readline.c index 1d0b009d548b82..acfc6e57d49555 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -126,6 +126,7 @@ readline_clear(PyObject *m) return 0; } +/* This undoes any readline configuration done in setup_readline(). */ static void readline_cleanup(void) { From 59239f0b6f6c3c35cbe5e4774cf834c16fd71462 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 10:10:52 +1100 Subject: [PATCH 10/16] Update Modules/readline.c Co-authored-by: Eric Snow --- Modules/readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/readline.c b/Modules/readline.c index acfc6e57d49555..1cc531c983dd73 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1245,7 +1245,7 @@ flex_complete(const char *text, int start, int end) size_t start_size, end_size; wchar_t *s; PyGILState_STATE gilstate = PyGILState_Ensure(); - readlinestate *state = get_global_readline_state(false); + readlinestate *state = get_hook_module_state(); #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER rl_completion_append_character ='\0'; #endif From cf72dc47ecc5f49de90a92d551b164ba67cee277 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 10:52:57 +1100 Subject: [PATCH 11/16] Apply suggestions from code review Co-authored-by: Eric Snow --- Modules/readline.c | 51 +++++++++++----------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 1cc531c983dd73..9b16c9058b6d28 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -462,10 +462,7 @@ readline_set_completion_display_matches_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); PyObject *result = set_hook("completion_display_matches_hook", &state->completion_display_matches_hook, function); @@ -500,10 +497,7 @@ static PyObject * readline_set_startup_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=02cd0e0c4fa082ad input=7783b4334b26d16d]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); return set_hook("startup_hook", &state->startup_hook, function); } @@ -529,10 +523,7 @@ static PyObject * readline_set_pre_input_hook_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=fe1a96505096f464 input=4f3eaeaf7ce1fdbe]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); return set_hook("pre_input_hook", &state->pre_input_hook, function); } @@ -566,10 +557,7 @@ static PyObject * readline_get_begidx_impl(PyObject *module) /*[clinic end generated code: output=362616ee8ed1b2b1 input=e083b81c8eb4bac3]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); return Py_NewRef(state->begidx); } @@ -585,10 +573,7 @@ static PyObject * readline_get_endidx_impl(PyObject *module) /*[clinic end generated code: output=7f763350b12d7517 input=d4c7e34a625fd770]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); return Py_NewRef(state->endidx); } @@ -816,10 +801,7 @@ static PyObject * readline_set_completer_impl(PyObject *module, PyObject *function) /*[clinic end generated code: output=171a2a60f81d3204 input=51e81e13118eb877]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); return set_hook("completer", &state->completer, function); } @@ -833,10 +815,7 @@ static PyObject * readline_get_completer_impl(PyObject *module) /*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/ { - readlinestate *state = get_global_readline_state_with_error(); - if (state == NULL) { - return NULL; - } + readlinestate *state = get_readline_state(module); if (state->completer == NULL) { Py_RETURN_NONE; } @@ -1078,18 +1057,12 @@ on_startup_hook(void) { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - PyObject* mod = PyState_FindModule(&readlinemodule); - if (mod == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); - } - readline_cleanup(); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { PyGILState_Release(gilstate); return -1; } - Py_INCREF(mod); - r = on_hook(get_readline_state(mod)->startup_hook); - Py_DECREF(mod); + r = on_hook(state->startup_hook); PyGILState_Release(gilstate); return r; } @@ -1134,7 +1107,7 @@ on_completion_display_matches_hook(char **matches, int i; PyObject *sub, *m=NULL, *s=NULL, *r=NULL; PyGILState_STATE gilstate = PyGILState_Ensure(); - readlinestate *state = get_global_readline_state(false); + readlinestate *state = get_hook_module_state(); if (state == NULL) { PyGILState_Release(gilstate); return; @@ -1198,7 +1171,7 @@ on_completion(const char *text, int state) { char *result = NULL; PyGILState_STATE gilstate = PyGILState_Ensure(); - readlinestate *module_state = get_global_readline_state(false); + readlinestate *state = get_hook_module_state(); if (module_state == NULL) { PyGILState_Release(gilstate); return NULL; From aa75a623849ea19f1e84104e8ff0d845ba815c9b Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 11:15:42 +1100 Subject: [PATCH 12/16] Update Modules/readline.c Co-authored-by: Eric Snow --- Modules/readline.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 9b16c9058b6d28..f44c883f7ac67d 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -159,21 +159,21 @@ readline_free(void *m) static PyModuleDef readlinemodule; static inline readlinestate* -get_global_readline_state(bool set_error) +get_hook_module_state(void) { PyObject *mod = PyState_FindModule(&readlinemodule); if (mod == NULL){ - if (set_error && !PyErr_Occurred()){ - PyErr_SetString(PyExc_RuntimeError, - "readline module state not initialized"); - } + /* The hook is always going to fail(?), so remove it (and all others). */ + readline_cleanup(); + PyErr_Clear(); return NULL; } - return get_readline_state(mod); + Py_INCREF(mod); + readlinestate *state = get_readline_state(mod); + Py_DECREF(mod); + return state; } -#define get_global_readline_state_with_error() get_global_readline_state(true) - /* Convert to/from multibyte C strings */ static PyObject * From 30c79d750f08cffeb67d192ab56ca4751c00125e Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 11:17:06 +1100 Subject: [PATCH 13/16] fixup variable name --- Modules/readline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/readline.c b/Modules/readline.c index 9b16c9058b6d28..2b4751f7b43d0a 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1171,7 +1171,7 @@ on_completion(const char *text, int state) { char *result = NULL; PyGILState_STATE gilstate = PyGILState_Ensure(); - readlinestate *state = get_hook_module_state(); + readlinestate *module_state = get_hook_module_state(); if (module_state == NULL) { PyGILState_Release(gilstate); return NULL; From b1fa25b2b14c61f43a8ca612a5cd68a5661b4fca Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 11:18:13 +1100 Subject: [PATCH 14/16] Use simpler assertion --- Modules/readline.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 6421f53122f399..a039151790db18 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -163,8 +163,8 @@ get_hook_module_state(void) { PyObject *mod = PyState_FindModule(&readlinemodule); if (mod == NULL){ - /* The hook is always going to fail(?), so remove it (and all others). */ - readline_cleanup(); + /* We already made sure the module is findable in setup_readline(). */ + assert(PyErr_Occurred()); PyErr_Clear(); return NULL; } From ff038f02379bc99755b74d6b9895d6f8f8a990ec Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 11:21:23 +1100 Subject: [PATCH 15/16] Simpler hook check --- Modules/readline.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index a039151790db18..49d24f7e242f99 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -126,17 +126,6 @@ readline_clear(PyObject *m) return 0; } -/* This undoes any readline configuration done in setup_readline(). */ -static void -readline_cleanup(void) -{ - rl_startup_hook = NULL; -#ifdef HAVE_RL_PRE_INPUT_HOOK - rl_pre_input_hook = NULL; -#endif - rl_attempted_completion_function = NULL; -} - static int readline_traverse(PyObject *m, visitproc visit, void *arg) { @@ -1079,18 +1068,12 @@ on_pre_input_hook(void) { int r; PyGILState_STATE gilstate = PyGILState_Ensure(); - PyObject* mod = PyState_FindModule(&readlinemodule); - if (mod == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ImportError, "readline import initialization failure during startup hook"); - } - readline_cleanup(); + readlinestate *state = get_hook_module_state(); + if (state == NULL) { PyGILState_Release(gilstate); return -1; } - Py_INCREF(mod); - r = on_hook(get_readline_state(mod)->pre_input_hook); - Py_DECREF(mod); + r = on_hook(state->pre_input_hook); PyGILState_Release(gilstate); return r; } From 43d609dcc4f60698c67c1e6f303bf1d261146a86 Mon Sep 17 00:00:00 2001 From: Anthony Shaw Date: Tue, 28 Nov 2023 12:31:03 +1100 Subject: [PATCH 16/16] Don't assert error check --- Modules/readline.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/readline.c b/Modules/readline.c index 49d24f7e242f99..209ac8bbcfbe78 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -152,8 +152,6 @@ get_hook_module_state(void) { PyObject *mod = PyState_FindModule(&readlinemodule); if (mod == NULL){ - /* We already made sure the module is findable in setup_readline(). */ - assert(PyErr_Occurred()); PyErr_Clear(); return NULL; }