]> granicus.if.org Git - python/commitdiff
Issue #15699: The readline module now uses PEP 3121-style module initialization,...
authorAntoine Pitrou <solipsis@pitrou.net>
Wed, 31 Jul 2013 19:52:53 +0000 (21:52 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Wed, 31 Jul 2013 19:52:53 +0000 (21:52 +0200)
Original patch by Robin Schreiber.

Misc/NEWS
Modules/readline.c

index c08193d2f0aa05f2a05450be99d5a686d1217df8..1ef477f09f1e118fde19717e1c20d303c310833a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -173,6 +173,10 @@ Core and Builtins
 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
   'SHA1' instead of 'SHA'.
 
index ffc29f6539dab077b1301bb1d31ad1900a3cbabf..ab24636e2ffe76731299398f336aac6265650b80 100644 (file)
@@ -6,6 +6,7 @@
 
 /* Standard definitions */
 #include "Python.h"
+#include <stddef.h>
 #include <setjmp.h>
 #include <signal.h>
 #include <errno.h>
@@ -74,6 +75,55 @@ on_completion_display_matches_hook(char **matches,
    (see issue #17289 for the motivation). */
 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 */
 
 static PyObject *
@@ -250,23 +300,21 @@ set_hook(const char *funcname, PyObject **hook_var, PyObject *args)
 
 /* 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
-static PyObject *pre_input_hook = NULL;
+
 #endif
 
 static PyObject *
 set_completion_display_matches_hook(PyObject *self, PyObject *args)
 {
     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
     /* We cannot set this hook globally, since it replaces the
        default completion display. */
     rl_completion_display_matches_hook =
-        completion_display_matches_hook ?
+        readlinestate_global->completion_display_matches_hook ?
 #if defined(_RL_FUNCTION_TYPEDEF)
         (rl_compdisp_func_t *)on_completion_display_matches_hook : 0;
 #else
@@ -287,7 +335,7 @@ once each time matches need to be displayed.");
 static PyObject *
 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,
@@ -304,7 +352,7 @@ before readline prints the first prompt.");
 static PyObject *
 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,
@@ -319,10 +367,10 @@ characters.");
 
 /* 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 */
@@ -342,8 +390,8 @@ Get the type of completion being attempted.");
 static PyObject *
 get_begidx(PyObject *self, PyObject *noarg)
 {
-    Py_INCREF(begidx);
-    return begidx;
+    Py_INCREF(readlinestate_global->begidx);
+    return readlinestate_global->begidx;
 }
 
 PyDoc_STRVAR(doc_get_begidx,
@@ -356,8 +404,8 @@ get the beginning index of the readline tab-completion scope");
 static PyObject *
 get_endidx(PyObject *self, PyObject *noarg)
 {
-    Py_INCREF(endidx);
-    return endidx;
+    Py_INCREF(readlinestate_global->endidx);
+    return readlinestate_global->endidx;
 }
 
 PyDoc_STRVAR(doc_get_endidx,
@@ -522,7 +570,7 @@ get the readline word delimiters for tab-completion");
 static PyObject *
 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,
@@ -536,11 +584,11 @@ It should return the next possible completion starting with 'text'.");
 static PyObject *
 get_completer(PyObject *self, PyObject *noargs)
 {
-    if (completer == NULL) {
+    if (readlinestate_global->completer == NULL) {
         Py_RETURN_NONE;
     }
-    Py_INCREF(completer);
-    return completer;
+    Py_INCREF(readlinestate_global->completer);
+    return readlinestate_global->completer;
 }
 
 PyDoc_STRVAR(doc_get_completer,
@@ -744,9 +792,6 @@ on_hook(PyObject *func)
     int result = 0;
     if (func != NULL) {
         PyObject *r;
-#ifdef WITH_THREAD
-        PyGILState_STATE gilstate = PyGILState_Ensure();
-#endif
         r = PyObject_CallFunction(func, NULL);
         if (r == NULL)
             goto error;
@@ -763,9 +808,6 @@ on_hook(PyObject *func)
         PyErr_Clear();
         Py_XDECREF(r);
       done:
-#ifdef WITH_THREAD
-        PyGILState_Release(gilstate);
-#endif
         return result;
     }
     return result;
@@ -774,14 +816,30 @@ on_hook(PyObject *func)
 static int
 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
 static int
 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
 
@@ -808,7 +866,7 @@ on_completion_display_matches_hook(char **matches,
         if (PyList_SetItem(m, i, s) == -1)
             goto error;
     }
-    r = PyObject_CallFunction(completion_display_matches_hook,
+    r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook,
                               "sOi", matches[0], m, max_length);
 
     Py_DECREF(m); m=NULL;
@@ -838,13 +896,13 @@ static char *
 on_completion(const char *text, int state)
 {
     char *result = NULL;
-    if (completer != NULL) {
+    if (readlinestate_global->completer != NULL) {
         PyObject *r;
 #ifdef WITH_THREAD
         PyGILState_STATE gilstate = PyGILState_Ensure();
 #endif
         rl_attempted_completion_over = 1;
-        r = PyObject_CallFunction(completer, "si", text, state);
+        r = PyObject_CallFunction(readlinestate_global->completer, "si", text, state);
         if (r == NULL)
             goto error;
         if (r == Py_None) {
@@ -877,24 +935,32 @@ on_completion(const char *text, int state)
 static char **
 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
     rl_completion_append_character ='\0';
 #endif
 #ifdef HAVE_RL_COMPLETION_SUPPRESS_APPEND
     rl_completion_suppress_append = 0;
 #endif
-    Py_XDECREF(begidx);
-    Py_XDECREF(endidx);
-    begidx = PyLong_FromLong((long) start);
-    endidx = PyLong_FromLong((long) end);
-    return completion_matches(text, *on_completion);
+    Py_XDECREF(readlinestate_global->begidx);
+    Py_XDECREF(readlinestate_global->endidx);
+    readlinestate_global->begidx = PyLong_FromLong((long) start);
+    readlinestate_global->endidx = PyLong_FromLong((long) end);
+    result = completion_matches(text, *on_completion);
+#ifdef WITH_THREAD
+    PyGILState_Release(gilstate);
+#endif
+    return result;
 }
 
 
 /* Helper to initialize GNU readline properly. */
 
 static void
-setup_readline(void)
+setup_readline(readlinestate *mod_state)
 {
 #ifdef SAVE_LOCALE
     char *saved_locale = strdup(setlocale(LC_CTYPE, NULL));
@@ -931,8 +997,8 @@ setup_readline(void)
         strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?");
         /* All nonalphanums except '.' */
 
-    begidx = PyLong_FromLong(0L);
-    endidx = PyLong_FromLong(0L);
+    mod_state->begidx = PyLong_FromLong(0L);
+    mod_state->endidx = PyLong_FromLong(0L);
     /* Initialize (allows .inputrc to override)
      *
      * XXX: A bug in the readline-2.2 library causes a memory leak
@@ -1154,12 +1220,12 @@ static struct PyModuleDef readlinemodule = {
     PyModuleDef_HEAD_INIT,
     "readline",
     doc_module,
-    -1,
+    sizeof(readlinestate),
     readline_methods,
     NULL,
-    NULL,
-    NULL,
-    NULL
+    readline_traverse,
+    readline_clear,
+    readline_free
 };
 
 
@@ -1167,6 +1233,7 @@ PyMODINIT_FUNC
 PyInit_readline(void)
 {
     PyObject *m;
+    readlinestate *mod_state;
 
 #ifdef __APPLE__
     if (strncmp(rl_library_version, libedit_version_tag, strlen(libedit_version_tag)) == 0) {
@@ -1183,7 +1250,8 @@ PyInit_readline(void)
     if (m == NULL)
         return NULL;
 
+    mod_state = (readlinestate *) PyModule_GetState(m);
     PyOS_ReadlineFunctionPointer = call_readline;
-    setup_readline();
+    setup_readline(mod_state);
     return m;
 }