Commit 7cc267a8 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #15699: The readline module now uses PEP 3121-style module...

Issue #15699: The readline module now uses PEP 3121-style module initialization, so as to reclaim allocated resources (Python callbacks) at shutdown.
Original patch by Robin Schreiber.
parent de78a77b
...@@ -173,6 +173,10 @@ Core and Builtins ...@@ -173,6 +173,10 @@ Core and Builtins
Library Library
------- -------
- Issue #15699: The readline module now uses PEP 3121-style module
initialization, so as to reclaim allocated resources (Python callbacks)
at shutdown. Original patch by Robin Schreiber.
- Issue #18599: Fix name attribute of _sha1.sha1() object. It now returns - Issue #18599: Fix name attribute of _sha1.sha1() object. It now returns
'SHA1' instead of 'SHA'. 'SHA1' instead of 'SHA'.
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
/* Standard definitions */ /* Standard definitions */
#include "Python.h" #include "Python.h"
#include <stddef.h>
#include <setjmp.h> #include <setjmp.h>
#include <signal.h> #include <signal.h>
#include <errno.h> #include <errno.h>
...@@ -74,6 +75,55 @@ on_completion_display_matches_hook(char **matches, ...@@ -74,6 +75,55 @@ on_completion_display_matches_hook(char **matches,
(see issue #17289 for the motivation). */ (see issue #17289 for the motivation). */
static char *completer_word_break_characters; static char *completer_word_break_characters;
typedef struct {
PyObject *completion_display_matches_hook;
PyObject *startup_hook;
PyObject *pre_input_hook;
PyObject *completer;
PyObject *begidx;
PyObject *endidx;
} readlinestate;
#define readline_state(o) ((readlinestate *)PyModule_GetState(o))
static int
readline_clear(PyObject *m)
{
readlinestate *state = readline_state(m);
Py_CLEAR(state->completion_display_matches_hook);
Py_CLEAR(state->startup_hook);
Py_CLEAR(state->pre_input_hook);
Py_CLEAR(state->completer);
Py_CLEAR(state->begidx);
Py_CLEAR(state->endidx);
return 0;
}
static int
readline_traverse(PyObject *m, visitproc visit, void *arg)
{
readlinestate *state = readline_state(m);
Py_VISIT(state->completion_display_matches_hook);
Py_VISIT(state->startup_hook);
Py_VISIT(state->pre_input_hook);
Py_VISIT(state->completer);
Py_VISIT(state->begidx);
Py_VISIT(state->endidx);
return 0;
}
static void
readline_free(void *m)
{
readline_clear((PyObject *)m);
}
static PyModuleDef readlinemodule;
#define readlinestate_global ((readlinestate *)PyModule_GetState(PyState_FindModule(&readlinemodule)))
/* Exported function to send one line to readline's init file parser */ /* Exported function to send one line to readline's init file parser */
static PyObject * static PyObject *
...@@ -250,23 +300,21 @@ set_hook(const char *funcname, PyObject **hook_var, PyObject *args) ...@@ -250,23 +300,21 @@ set_hook(const char *funcname, PyObject **hook_var, PyObject *args)
/* Exported functions to specify hook functions in Python */ /* Exported functions to specify hook functions in Python */
static PyObject *completion_display_matches_hook = NULL;
static PyObject *startup_hook = NULL;
#ifdef HAVE_RL_PRE_INPUT_HOOK #ifdef HAVE_RL_PRE_INPUT_HOOK
static PyObject *pre_input_hook = NULL;
#endif #endif
static PyObject * static PyObject *
set_completion_display_matches_hook(PyObject *self, PyObject *args) set_completion_display_matches_hook(PyObject *self, PyObject *args)
{ {
PyObject *result = set_hook("completion_display_matches_hook", PyObject *result = set_hook("completion_display_matches_hook",
&completion_display_matches_hook, args); &readlinestate_global->completion_display_matches_hook, args);
#ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK #ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK
/* We cannot set this hook globally, since it replaces the /* We cannot set this hook globally, since it replaces the
default completion display. */ default completion display. */
rl_completion_display_matches_hook = rl_completion_display_matches_hook =
completion_display_matches_hook ? readlinestate_global->completion_display_matches_hook ?
#if defined(_RL_FUNCTION_TYPEDEF) #if defined(_RL_FUNCTION_TYPEDEF)
(rl_compdisp_func_t *)on_completion_display_matches_hook : 0; (rl_compdisp_func_t *)on_completion_display_matches_hook : 0;
#else #else
...@@ -287,7 +335,7 @@ once each time matches need to be displayed."); ...@@ -287,7 +335,7 @@ once each time matches need to be displayed.");
static PyObject * static PyObject *
set_startup_hook(PyObject *self, PyObject *args) set_startup_hook(PyObject *self, PyObject *args)
{ {
return set_hook("startup_hook", &startup_hook, args); return set_hook("startup_hook", &readlinestate_global->startup_hook, args);
} }
PyDoc_STRVAR(doc_set_startup_hook, PyDoc_STRVAR(doc_set_startup_hook,
...@@ -304,7 +352,7 @@ before readline prints the first prompt."); ...@@ -304,7 +352,7 @@ before readline prints the first prompt.");
static PyObject * static PyObject *
set_pre_input_hook(PyObject *self, PyObject *args) set_pre_input_hook(PyObject *self, PyObject *args)
{ {
return set_hook("pre_input_hook", &pre_input_hook, args); return set_hook("pre_input_hook", &readlinestate_global->pre_input_hook, args);
} }
PyDoc_STRVAR(doc_set_pre_input_hook, PyDoc_STRVAR(doc_set_pre_input_hook,
...@@ -319,10 +367,10 @@ characters."); ...@@ -319,10 +367,10 @@ characters.");
/* Exported function to specify a word completer in Python */ /* Exported function to specify a word completer in Python */
static PyObject *completer = NULL;
static PyObject *begidx = NULL;
static PyObject *endidx = NULL;
/* Get the completion type for the scope of the tab-completion */ /* Get the completion type for the scope of the tab-completion */
...@@ -342,8 +390,8 @@ Get the type of completion being attempted."); ...@@ -342,8 +390,8 @@ Get the type of completion being attempted.");
static PyObject * static PyObject *
get_begidx(PyObject *self, PyObject *noarg) get_begidx(PyObject *self, PyObject *noarg)
{ {
Py_INCREF(begidx); Py_INCREF(readlinestate_global->begidx);
return begidx; return readlinestate_global->begidx;
} }
PyDoc_STRVAR(doc_get_begidx, PyDoc_STRVAR(doc_get_begidx,
...@@ -356,8 +404,8 @@ get the beginning index of the readline tab-completion scope"); ...@@ -356,8 +404,8 @@ get the beginning index of the readline tab-completion scope");
static PyObject * static PyObject *
get_endidx(PyObject *self, PyObject *noarg) get_endidx(PyObject *self, PyObject *noarg)
{ {
Py_INCREF(endidx); Py_INCREF(readlinestate_global->endidx);
return endidx; return readlinestate_global->endidx;
} }
PyDoc_STRVAR(doc_get_endidx, PyDoc_STRVAR(doc_get_endidx,
...@@ -522,7 +570,7 @@ get the readline word delimiters for tab-completion"); ...@@ -522,7 +570,7 @@ get the readline word delimiters for tab-completion");
static PyObject * static PyObject *
set_completer(PyObject *self, PyObject *args) set_completer(PyObject *self, PyObject *args)
{ {
return set_hook("completer", &completer, args); return set_hook("completer", &readlinestate_global->completer, args);
} }
PyDoc_STRVAR(doc_set_completer, PyDoc_STRVAR(doc_set_completer,
...@@ -536,11 +584,11 @@ It should return the next possible completion starting with 'text'."); ...@@ -536,11 +584,11 @@ It should return the next possible completion starting with 'text'.");
static PyObject * static PyObject *
get_completer(PyObject *self, PyObject *noargs) get_completer(PyObject *self, PyObject *noargs)
{ {
if (completer == NULL) { if (readlinestate_global->completer == NULL) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
Py_INCREF(completer); Py_INCREF(readlinestate_global->completer);
return completer; return readlinestate_global->completer;
} }
PyDoc_STRVAR(doc_get_completer, PyDoc_STRVAR(doc_get_completer,
...@@ -744,9 +792,6 @@ on_hook(PyObject *func) ...@@ -744,9 +792,6 @@ on_hook(PyObject *func)
int result = 0; int result = 0;
if (func != NULL) { if (func != NULL) {
PyObject *r; PyObject *r;
#ifdef WITH_THREAD
PyGILState_STATE gilstate = PyGILState_Ensure();
#endif
r = PyObject_CallFunction(func, NULL); r = PyObject_CallFunction(func, NULL);
if (r == NULL) if (r == NULL)
goto error; goto error;
...@@ -763,9 +808,6 @@ on_hook(PyObject *func) ...@@ -763,9 +808,6 @@ on_hook(PyObject *func)
PyErr_Clear(); PyErr_Clear();
Py_XDECREF(r); Py_XDECREF(r);
done: done:
#ifdef WITH_THREAD
PyGILState_Release(gilstate);
#endif
return result; return result;
} }
return result; return result;
...@@ -774,14 +816,30 @@ on_hook(PyObject *func) ...@@ -774,14 +816,30 @@ on_hook(PyObject *func)
static int static int
on_startup_hook(void) on_startup_hook(void)
{ {
return on_hook(startup_hook); int r;
#ifdef WITH_THREAD
PyGILState_STATE gilstate = PyGILState_Ensure();
#endif
r = on_hook(readlinestate_global->startup_hook);
#ifdef WITH_THREAD
PyGILState_Release(gilstate);
#endif
return r;
} }
#ifdef HAVE_RL_PRE_INPUT_HOOK #ifdef HAVE_RL_PRE_INPUT_HOOK
static int static int
on_pre_input_hook(void) on_pre_input_hook(void)
{ {
return on_hook(pre_input_hook); int r;
#ifdef WITH_THREAD
PyGILState_STATE gilstate = PyGILState_Ensure();
#endif
r = on_hook(readlinestate_global->pre_input_hook);
#ifdef WITH_THREAD
PyGILState_Release(gilstate);
#endif
return r;
} }
#endif #endif
...@@ -808,7 +866,7 @@ on_completion_display_matches_hook(char **matches, ...@@ -808,7 +866,7 @@ on_completion_display_matches_hook(char **matches,
if (PyList_SetItem(m, i, s) == -1) if (PyList_SetItem(m, i, s) == -1)
goto error; goto error;
} }
r = PyObject_CallFunction(completion_display_matches_hook, r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook,
"sOi", matches[0], m, max_length); "sOi", matches[0], m, max_length);
Py_DECREF(m); m=NULL; Py_DECREF(m); m=NULL;
...@@ -838,13 +896,13 @@ static char * ...@@ -838,13 +896,13 @@ static char *
on_completion(const char *text, int state) on_completion(const char *text, int state)
{ {
char *result = NULL; char *result = NULL;
if (completer != NULL) { if (readlinestate_global->completer != NULL) {
PyObject *r; PyObject *r;
#ifdef WITH_THREAD #ifdef WITH_THREAD
PyGILState_STATE gilstate = PyGILState_Ensure(); PyGILState_STATE gilstate = PyGILState_Ensure();
#endif #endif
rl_attempted_completion_over = 1; rl_attempted_completion_over = 1;
r = PyObject_CallFunction(completer, "si", text, state); r = PyObject_CallFunction(readlinestate_global->completer, "si", text, state);
if (r == NULL) if (r == NULL)
goto error; goto error;
if (r == Py_None) { if (r == Py_None) {
...@@ -877,24 +935,32 @@ on_completion(const char *text, int state) ...@@ -877,24 +935,32 @@ on_completion(const char *text, int state)
static char ** static char **
flex_complete(char *text, int start, int end) flex_complete(char *text, int start, int end)
{ {
char **result;
#ifdef WITH_THREAD
PyGILState_STATE gilstate = PyGILState_Ensure();
#endif
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
rl_completion_append_character ='\0'; rl_completion_append_character ='\0';
#endif #endif
#ifdef HAVE_RL_COMPLETION_SUPPRESS_APPEND #ifdef HAVE_RL_COMPLETION_SUPPRESS_APPEND
rl_completion_suppress_append = 0; rl_completion_suppress_append = 0;
#endif #endif
Py_XDECREF(begidx); Py_XDECREF(readlinestate_global->begidx);
Py_XDECREF(endidx); Py_XDECREF(readlinestate_global->endidx);
begidx = PyLong_FromLong((long) start); readlinestate_global->begidx = PyLong_FromLong((long) start);
endidx = PyLong_FromLong((long) end); readlinestate_global->endidx = PyLong_FromLong((long) end);
return completion_matches(text, *on_completion); result = completion_matches(text, *on_completion);
#ifdef WITH_THREAD
PyGILState_Release(gilstate);
#endif
return result;
} }
/* Helper to initialize GNU readline properly. */ /* Helper to initialize GNU readline properly. */
static void static void
setup_readline(void) setup_readline(readlinestate *mod_state)
{ {
#ifdef SAVE_LOCALE #ifdef SAVE_LOCALE
char *saved_locale = strdup(setlocale(LC_CTYPE, NULL)); char *saved_locale = strdup(setlocale(LC_CTYPE, NULL));
...@@ -931,8 +997,8 @@ setup_readline(void) ...@@ -931,8 +997,8 @@ setup_readline(void)
strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"); strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?");
/* All nonalphanums except '.' */ /* All nonalphanums except '.' */
begidx = PyLong_FromLong(0L); mod_state->begidx = PyLong_FromLong(0L);
endidx = PyLong_FromLong(0L); mod_state->endidx = PyLong_FromLong(0L);
/* Initialize (allows .inputrc to override) /* Initialize (allows .inputrc to override)
* *
* XXX: A bug in the readline-2.2 library causes a memory leak * XXX: A bug in the readline-2.2 library causes a memory leak
...@@ -1154,12 +1220,12 @@ static struct PyModuleDef readlinemodule = { ...@@ -1154,12 +1220,12 @@ static struct PyModuleDef readlinemodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"readline", "readline",
doc_module, doc_module,
-1, sizeof(readlinestate),
readline_methods, readline_methods,
NULL, NULL,
NULL, readline_traverse,
NULL, readline_clear,
NULL readline_free
}; };
...@@ -1167,6 +1233,7 @@ PyMODINIT_FUNC ...@@ -1167,6 +1233,7 @@ PyMODINIT_FUNC
PyInit_readline(void) PyInit_readline(void)
{ {
PyObject *m; PyObject *m;
readlinestate *mod_state;
#ifdef __APPLE__ #ifdef __APPLE__
if (strncmp(rl_library_version, libedit_version_tag, strlen(libedit_version_tag)) == 0) { if (strncmp(rl_library_version, libedit_version_tag, strlen(libedit_version_tag)) == 0) {
...@@ -1183,7 +1250,8 @@ PyInit_readline(void) ...@@ -1183,7 +1250,8 @@ PyInit_readline(void)
if (m == NULL) if (m == NULL)
return NULL; return NULL;
mod_state = (readlinestate *) PyModule_GetState(m);
PyOS_ReadlineFunctionPointer = call_readline; PyOS_ReadlineFunctionPointer = call_readline;
setup_readline(); setup_readline(mod_state);
return m; return m;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment