[Python-checkins] cpython: Issue #15699: The readline module now uses PEP 3121-style module

antoine.pitrou python-checkins at python.org
Wed Jul 31 21:54:28 CEST 2013


http://hg.python.org/cpython/rev/e4594c7dfeeb
changeset:   84934:e4594c7dfeeb
user:        Antoine Pitrou <solipsis at pitrou.net>
date:        Wed Jul 31 21:52:53 2013 +0200
summary:
  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.

files:
  Misc/NEWS          |    4 +
  Modules/readline.c |  152 +++++++++++++++++++++++---------
  2 files changed, 114 insertions(+), 42 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -173,6 +173,10 @@
 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'.
 
diff --git a/Modules/readline.c b/Modules/readline.c
--- a/Modules/readline.c
+++ b/Modules/readline.c
@@ -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 @@
    (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 @@
 
 /* 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 @@
 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 @@
 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 @@
 
 /* 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
     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 @@
         PyErr_Clear();
         Py_XDECREF(r);
       done:
-#ifdef WITH_THREAD
-        PyGILState_Release(gilstate);
-#endif
         return result;
     }
     return result;
@@ -774,14 +816,30 @@
 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 @@
         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 @@
 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 @@
 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 @@
         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 @@
     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 @@
 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 @@
     if (m == NULL)
         return NULL;
 
+    mod_state = (readlinestate *) PyModule_GetState(m);
     PyOS_ReadlineFunctionPointer = call_readline;
-    setup_readline();
+    setup_readline(mod_state);
     return m;
 }

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list