From 0daad598d0947395248f087c2aa347083f440e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 30 Sep 2001 21:09:59 +0000 Subject: Patch #462122: add readline startup and pre_event hooks. --- Doc/lib/libreadline.tex | 16 +++++ Misc/NEWS | 2 + Modules/readline.c | 151 ++++++++++++++++++++++++++++++++++++++++-------- acconfig.h | 3 + configure | 63 +++++++++++++++++--- configure.in | 4 ++ pyconfig.h.in | 3 + 7 files changed, 209 insertions(+), 33 deletions(-) diff --git a/Doc/lib/libreadline.tex b/Doc/lib/libreadline.tex index a357144..7350845 100644 --- a/Doc/lib/libreadline.tex +++ b/Doc/lib/libreadline.tex @@ -54,6 +54,22 @@ history file when saving. Negative values imply unlimited history file size. \end{funcdesc} +\begin{funcdesc}{set_startup_hook}{\optional{function}} +Set or remove the startup_hook function. If \var{function} is specified, +it will be used as the new startup_hook function; if omitted or +\code{None}, any hook function already installed is removed. The +startup_hook function is called with no arguments just +before readline prints the first prompt. +\end{funcdesc} + +\begin{funcdesc}{set_pre_input_hook}{\optional{function}} +Set or remove the pre_input_hook function. If \var{function} is specified, +it will be used as the new pre_input_hook function; if omitted or +\code{None}, any hook function already installed is removed. The +pre_input_hook function is called with no arguments after the first prompt +has been printed and just before readline starts reading input characters. +\end{funcdesc} + \begin{funcdesc}{set_completer}{\optional{function}} Set or remove the completer function. If \var{function} is specified, it will be used as the new completer function; if omitted or diff --git a/Misc/NEWS b/Misc/NEWS index 4b6ae5b..b7ccc86 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -8,6 +8,8 @@ Core - binascii has now two quopri support functions, a2b_qp and b2a_qp. +- readline now supports setting the startup_hook and the pre_event_hook. + Library - quopri's encode and decode methods take an optional header parameter, diff --git a/Modules/readline.c b/Modules/readline.c index 6469d27..49839c4 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -158,12 +158,80 @@ get_history_length(PyObject *self, PyObject *args) return Py_BuildValue("i", history_length); } +/* Generic hook function setter */ +static PyObject * +set_hook(const char * funcname, PyObject **hook_var, PyThreadState **tstate, PyObject *args) +{ + PyObject *function = Py_None; + char buf[80]; + sprintf(buf, "|O:set_%s", funcname); + if (!PyArg_ParseTuple(args, buf, &function)) + return NULL; + if (function == Py_None) { + Py_XDECREF(*hook_var); + *hook_var = NULL; + *tstate = NULL; + } + else if (PyCallable_Check(function)) { + PyObject *tmp = *hook_var; + Py_INCREF(function); + *hook_var = function; + Py_XDECREF(tmp); + *tstate = PyThreadState_Get(); + } + else { + sprintf(buf, "set_%s(func): argument not callable", funcname); + PyErr_SetString(PyExc_TypeError, buf); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +/* Exported functions to specify hook functions in Python */ + +static PyObject *startup_hook = NULL; +static PyThreadState *startup_hook_tstate = NULL; + +#ifdef HAVE_RL_PRE_INPUT_HOOK +static PyObject *pre_input_hook = NULL; +static PyThreadState *pre_input_hook_tstate = NULL; +#endif + +static PyObject * +set_startup_hook(PyObject *self, PyObject *args) +{ + return set_hook("startup_hook", &startup_hook, &startup_hook_tstate, args); +} + +static char doc_set_startup_hook[] = "\ +set_startup_hook([function]) -> None\n\ +Set or remove the startup_hook function.\n\ +The function is called with no arguments just\n\ +before readline prints the first prompt.\n\ +"; + +#ifdef HAVE_RL_PRE_INPUT_HOOK +static PyObject * +set_pre_input_hook(PyObject *self, PyObject *args) +{ + return set_hook("pre_input_hook", &pre_input_hook, &pre_input_hook_tstate, args); +} + +static char doc_set_pre_input_hook[] = "\ +set_pre_input_hook([function]) -> None\n\ +Set or remove the pre_input_hook function.\n\ +The function is called with no arguments after the first prompt\n\ +has been printed and just before readline starts reading input\n\ +characters.\n\ +"; +#endif /* Exported function to specify a word completer in Python */ static PyObject *completer = NULL; -static PyThreadState *tstate = NULL; +static PyThreadState *completer_tstate = NULL; static PyObject *begidx = NULL; static PyObject *endidx = NULL; @@ -238,28 +306,7 @@ get the readline word delimiters for tab-completion"; static PyObject * set_completer(PyObject *self, PyObject *args) { - PyObject *function = Py_None; - if (!PyArg_ParseTuple(args, "|O:set_completer", &function)) - return NULL; - if (function == Py_None) { - Py_XDECREF(completer); - completer = NULL; - tstate = NULL; - } - else if (PyCallable_Check(function)) { - PyObject *tmp = completer; - Py_INCREF(function); - completer = function; - Py_XDECREF(tmp); - tstate = PyThreadState_Get(); - } - else { - PyErr_SetString(PyExc_TypeError, - "set_completer(func): argument not callable"); - return NULL; - } - Py_INCREF(Py_None); - return Py_None; + return set_hook("completer", &completer, &completer_tstate, args); } static char doc_set_completer[] = "\ @@ -330,9 +377,60 @@ static struct PyMethodDef readline_methods[] = METH_VARARGS, doc_set_completer_delims}, {"get_completer_delims", get_completer_delims, METH_OLDARGS, doc_get_completer_delims}, + + {"set_startup_hook", set_startup_hook, METH_VARARGS, doc_set_startup_hook}, +#ifdef HAVE_RL_PRE_INPUT_HOOK + {"set_pre_input_hook", set_pre_input_hook, METH_VARARGS, doc_set_pre_input_hook}, +#endif {0, 0} }; +/* C function to call the Python hooks. */ + +static int +on_hook(PyObject *func, PyThreadState *tstate) +{ + int result = 0; + if (func != NULL) { + PyObject *r; + PyThreadState *save_tstate; + /* Note that readline is called with the interpreter + lock released! */ + save_tstate = PyThreadState_Swap(NULL); + PyEval_RestoreThread(tstate); + r = PyObject_CallFunction(func, NULL); + if (r == NULL) + goto error; + if (r == Py_None) + result = 0; + else + result = PyInt_AsLong(r); + Py_DECREF(r); + goto done; + error: + PyErr_Clear(); + Py_XDECREF(r); + done: + PyEval_SaveThread(); + PyThreadState_Swap(save_tstate); + } + return result; +} + +static int +on_startup_hook(void) +{ + return on_hook(startup_hook, startup_hook_tstate); +} + +#ifdef HAVE_RL_PRE_INPUT_HOOK +static int +on_pre_input_hook(void) +{ + return on_hook(pre_input_hook, pre_input_hook_tstate); +} +#endif + /* C function to call the Python completer. */ static char * @@ -345,7 +443,7 @@ on_completion(char *text, int state) /* Note that readline is called with the interpreter lock released! */ save_tstate = PyThreadState_Swap(NULL); - PyEval_RestoreThread(tstate); + PyEval_RestoreThread(completer_tstate); r = PyObject_CallFunction(completer, "si", text, state); if (r == NULL) goto error; @@ -395,6 +493,11 @@ setup_readline(void) /* Bind both ESC-TAB and ESC-ESC to the completion function */ rl_bind_key_in_map ('\t', rl_complete, emacs_meta_keymap); rl_bind_key_in_map ('\033', rl_complete, emacs_meta_keymap); + /* Set our hook functions */ + rl_startup_hook = (Function *)on_startup_hook; +#ifdef HAVE_RL_PRE_INPUT_HOOK + rl_pre_input_hook = (Function *)on_pre_input_hook; +#endif /* Set our completion function */ rl_attempted_completion_function = (CPPFunction *)flex_complete; /* Set Python word break characters */ diff --git a/acconfig.h b/acconfig.h index ded68bb..9e30ab5 100644 --- a/acconfig.h +++ b/acconfig.h @@ -87,6 +87,9 @@ /* Define if you have GNU PTH threads */ #undef HAVE_PTH +/* Define if you have readline 4.0 */ +#undef HAVE_RL_PRE_INPUT_HOOK + /* Define if you have readline 4.2 */ #undef HAVE_RL_COMPLETION_MATCHES diff --git a/configure b/configure index ca245d3..d4af9b2 100755 --- a/configure +++ b/configure @@ -7027,9 +7027,54 @@ EOF fi +# check for readline 4.0 +echo $ac_n "checking for rl_pre_input_hook in -lreadline""... $ac_c" 1>&6 +echo "configure:7033: checking for rl_pre_input_hook in -lreadline" >&5 +ac_lib_var=`echo readline'_'rl_pre_input_hook | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lreadline -ltermcap $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_RL_PRE_INPUT_HOOK 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + + # check for readline 4.2 echo $ac_n "checking for rl_completion_matches in -lreadline""... $ac_c" 1>&6 -echo "configure:7033: checking for rl_completion_matches in -lreadline" >&5 +echo "configure:7078: checking for rl_completion_matches in -lreadline" >&5 ac_lib_var=`echo readline'_'rl_completion_matches | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -7037,7 +7082,7 @@ else ac_save_LIBS="$LIBS" LIBS="-lreadline -ltermcap $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:7097: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -7073,7 +7118,7 @@ fi echo $ac_n "checking for broken nice()""... $ac_c" 1>&6 -echo "configure:7077: checking for broken nice()" >&5 +echo "configure:7122: checking for broken nice()" >&5 if eval "test \"`echo '$''{'ac_cv_broken_nice'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -7082,7 +7127,7 @@ if test "$cross_compiling" = yes; then ac_cv_broken_nice=no else cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +if { (eval echo configure:7143: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_broken_nice=yes else @@ -7125,12 +7170,12 @@ cat >> confdefs.h <<\EOF #endif EOF echo $ac_n "checking for socklen_t""... $ac_c" 1>&6 -echo "configure:7129: checking for socklen_t" >&5 +echo "configure:7174: checking for socklen_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_socklen_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS @@ -7179,7 +7224,7 @@ done SRCDIRS="Parser Grammar Objects Python Modules" echo $ac_n "checking for build directories""... $ac_c" 1>&6 -echo "configure:7183: checking for build directories" >&5 +echo "configure:7228: checking for build directories" >&5 for dir in $SRCDIRS; do if test ! -d $dir; then mkdir $dir diff --git a/configure.in b/configure.in index 31412a1..d8fffc0 100644 --- a/configure.in +++ b/configure.in @@ -1832,6 +1832,10 @@ then AC_DEFINE(HAVE_GETC_UNLOCKED) fi +# check for readline 4.0 +AC_CHECK_LIB(readline, rl_pre_input_hook, + AC_DEFINE(HAVE_RL_PRE_INPUT_HOOK), , -ltermcap) + # check for readline 4.2 AC_CHECK_LIB(readline, rl_completion_matches, AC_DEFINE(HAVE_RL_COMPLETION_MATCHES), , -ltermcap) diff --git a/pyconfig.h.in b/pyconfig.h.in index 9250e66..00dea75 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -149,6 +149,9 @@ /* Define if you have GNU PTH threads */ #undef HAVE_PTH +/* Define if you have readline 4.0 */ +#undef HAVE_RL_PRE_INPUT_HOOK + /* Define if you have readline 4.2 */ #undef HAVE_RL_COMPLETION_MATCHES -- cgit v0.12