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.
This commit is contained in:
parent
32d483c39b
commit
5c30a75722
@ -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 @@
|
|||||||
|
|
||||||
/* 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,
|
|||||||
(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)
|
|||||||
|
|
||||||
/* 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.");
|
|||||||
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.");
|
|||||||
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.");
|
|||||||
|
|
||||||
/* 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.");
|
|||||||
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");
|
|||||||
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");
|
|||||||
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'.");
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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 *
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 = {
|
|||||||
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
|
|||||||
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)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user