Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-112292 : Catch import error conditions with readline hooks #112313

Merged
merged 19 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash in :mod:`readline` when imported from a sub interpreter. Patch
by Anthony Shaw
91 changes: 69 additions & 22 deletions Modules/readline.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,19 @@ readline_free(void *m)

static PyModuleDef readlinemodule;

#define readlinestate_global ((readlinestate *)PyModule_GetState(PyState_FindModule(&readlinemodule)))

static inline readlinestate*
get_hook_module_state(void)
{
PyObject *mod = PyState_FindModule(&readlinemodule);
if (mod == NULL){
PyErr_Clear();
return NULL;
}
Py_INCREF(mod);
readlinestate *state = get_readline_state(mod);
Py_DECREF(mod);
return state;
}

/* Convert to/from multibyte C strings */

Expand Down Expand Up @@ -438,14 +449,15 @@ readline_set_completion_display_matches_hook_impl(PyObject *module,
PyObject *function)
/*[clinic end generated code: output=516e5cb8db75a328 input=4f0bfd5ab0179a26]*/
{
readlinestate *state = get_readline_state(module);
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
Expand All @@ -472,7 +484,8 @@ 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_readline_state(module);
return set_hook("startup_hook", &state->startup_hook,
function);
}

Expand All @@ -497,7 +510,8 @@ 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_readline_state(module);
return set_hook("pre_input_hook", &state->pre_input_hook,
function);
}
#endif
Expand Down Expand Up @@ -530,7 +544,8 @@ 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_readline_state(module);
return Py_NewRef(state->begidx);
}

/* Get the ending index for the scope of the tab-completion */
Expand All @@ -545,7 +560,8 @@ 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_readline_state(module);
return Py_NewRef(state->endidx);
}

/* Set the tab-completion word-delimiters that readline uses */
Expand Down Expand Up @@ -772,7 +788,8 @@ 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_readline_state(module);
return set_hook("completer", &state->completer, function);
}

/*[clinic input]
Expand All @@ -785,10 +802,11 @@ static PyObject *
readline_get_completer_impl(PyObject *module)
/*[clinic end generated code: output=6e6bbd8226d14475 input=6457522e56d70d13]*/
{
if (readlinestate_global->completer == NULL) {
readlinestate *state = get_readline_state(module);
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
Expand Down Expand Up @@ -1026,7 +1044,12 @@ on_startup_hook(void)
{
int r;
PyGILState_STATE gilstate = PyGILState_Ensure();
r = on_hook(readlinestate_global->startup_hook);
tonybaloney marked this conversation as resolved.
Show resolved Hide resolved
readlinestate *state = get_hook_module_state();
if (state == NULL) {
PyGILState_Release(gilstate);
return -1;
}
r = on_hook(state->startup_hook);
PyGILState_Release(gilstate);
return r;
}
Expand All @@ -1043,7 +1066,12 @@ on_pre_input_hook(void)
{
int r;
PyGILState_STATE gilstate = PyGILState_Ensure();
r = on_hook(readlinestate_global->pre_input_hook);
readlinestate *state = get_hook_module_state();
if (state == NULL) {
PyGILState_Release(gilstate);
return -1;
}
r = on_hook(state->pre_input_hook);
PyGILState_Release(gilstate);
return r;
}
Expand All @@ -1060,6 +1088,11 @@ 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_hook_module_state();
if (state == NULL) {
PyGILState_Release(gilstate);
return;
}
m = PyList_New(num_matches);
if (m == NULL)
goto error;
Expand All @@ -1070,7 +1103,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;
Expand Down Expand Up @@ -1118,12 +1151,17 @@ static char *
on_completion(const char *text, int state)
{
char *result = NULL;
if (readlinestate_global->completer != NULL) {
PyGILState_STATE gilstate = PyGILState_Ensure();
tonybaloney marked this conversation as resolved.
Show resolved Hide resolved
readlinestate *module_state = get_hook_module_state();
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(readlinestate_global->completer, "Ni", t, state);
r = PyObject_CallFunction(module_state->completer, "Ni", t, state);
if (r == NULL)
goto error;
if (r == Py_None) {
Expand All @@ -1145,6 +1183,7 @@ on_completion(const char *text, int state)
PyGILState_Release(gilstate);
return result;
}
PyGILState_Release(gilstate);
return result;
}

Expand All @@ -1160,6 +1199,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_hook_module_state();
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
rl_completion_append_character ='\0';
#endif
Expand Down Expand Up @@ -1187,10 +1227,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;
Expand Down Expand Up @@ -1511,12 +1553,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:
Expand Down