Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0daad59

Browse files
committed
Patch #462122: add readline startup and pre_event hooks.
1 parent 16dc7f4 commit 0daad59

7 files changed

Lines changed: 209 additions & 33 deletions

File tree

Doc/lib/libreadline.tex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ \section{\module{readline} ---
5454
file size.
5555
\end{funcdesc}
5656

57+
\begin{funcdesc}{set_startup_hook}{\optional{function}}
58+
Set or remove the startup_hook function. If \var{function} is specified,
59+
it will be used as the new startup_hook function; if omitted or
60+
\code{None}, any hook function already installed is removed. The
61+
startup_hook function is called with no arguments just
62+
before readline prints the first prompt.
63+
\end{funcdesc}
64+
65+
\begin{funcdesc}{set_pre_input_hook}{\optional{function}}
66+
Set or remove the pre_input_hook function. If \var{function} is specified,
67+
it will be used as the new pre_input_hook function; if omitted or
68+
\code{None}, any hook function already installed is removed. The
69+
pre_input_hook function is called with no arguments after the first prompt
70+
has been printed and just before readline starts reading input characters.
71+
\end{funcdesc}
72+
5773
\begin{funcdesc}{set_completer}{\optional{function}}
5874
Set or remove the completer function. If \var{function} is specified,
5975
it will be used as the new completer function; if omitted or

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Core
88

99
- binascii has now two quopri support functions, a2b_qp and b2a_qp.
1010

11+
- readline now supports setting the startup_hook and the pre_event_hook.
12+
1113
Library
1214

1315
- quopri's encode and decode methods take an optional header parameter,

Modules/readline.c

Lines changed: 127 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,80 @@ get_history_length(PyObject *self, PyObject *args)
158158
return Py_BuildValue("i", history_length);
159159
}
160160

161+
/* Generic hook function setter */
161162

163+
static PyObject *
164+
set_hook(const char * funcname, PyObject **hook_var, PyThreadState **tstate, PyObject *args)
165+
{
166+
PyObject *function = Py_None;
167+
char buf[80];
168+
sprintf(buf, "|O:set_%s", funcname);
169+
if (!PyArg_ParseTuple(args, buf, &function))
170+
return NULL;
171+
if (function == Py_None) {
172+
Py_XDECREF(*hook_var);
173+
*hook_var = NULL;
174+
*tstate = NULL;
175+
}
176+
else if (PyCallable_Check(function)) {
177+
PyObject *tmp = *hook_var;
178+
Py_INCREF(function);
179+
*hook_var = function;
180+
Py_XDECREF(tmp);
181+
*tstate = PyThreadState_Get();
182+
}
183+
else {
184+
sprintf(buf, "set_%s(func): argument not callable", funcname);
185+
PyErr_SetString(PyExc_TypeError, buf);
186+
return NULL;
187+
}
188+
Py_INCREF(Py_None);
189+
return Py_None;
190+
}
191+
192+
/* Exported functions to specify hook functions in Python */
193+
194+
static PyObject *startup_hook = NULL;
195+
static PyThreadState *startup_hook_tstate = NULL;
196+
197+
#ifdef HAVE_RL_PRE_INPUT_HOOK
198+
static PyObject *pre_input_hook = NULL;
199+
static PyThreadState *pre_input_hook_tstate = NULL;
200+
#endif
201+
202+
static PyObject *
203+
set_startup_hook(PyObject *self, PyObject *args)
204+
{
205+
return set_hook("startup_hook", &startup_hook, &startup_hook_tstate, args);
206+
}
207+
208+
static char doc_set_startup_hook[] = "\
209+
set_startup_hook([function]) -> None\n\
210+
Set or remove the startup_hook function.\n\
211+
The function is called with no arguments just\n\
212+
before readline prints the first prompt.\n\
213+
";
214+
215+
#ifdef HAVE_RL_PRE_INPUT_HOOK
216+
static PyObject *
217+
set_pre_input_hook(PyObject *self, PyObject *args)
218+
{
219+
return set_hook("pre_input_hook", &pre_input_hook, &pre_input_hook_tstate, args);
220+
}
221+
222+
static char doc_set_pre_input_hook[] = "\
223+
set_pre_input_hook([function]) -> None\n\
224+
Set or remove the pre_input_hook function.\n\
225+
The function is called with no arguments after the first prompt\n\
226+
has been printed and just before readline starts reading input\n\
227+
characters.\n\
228+
";
229+
#endif
162230

163231
/* Exported function to specify a word completer in Python */
164232

165233
static PyObject *completer = NULL;
166-
static PyThreadState *tstate = NULL;
234+
static PyThreadState *completer_tstate = NULL;
167235

168236
static PyObject *begidx = NULL;
169237
static PyObject *endidx = NULL;
@@ -238,28 +306,7 @@ get the readline word delimiters for tab-completion";
238306
static PyObject *
239307
set_completer(PyObject *self, PyObject *args)
240308
{
241-
PyObject *function = Py_None;
242-
if (!PyArg_ParseTuple(args, "|O:set_completer", &function))
243-
return NULL;
244-
if (function == Py_None) {
245-
Py_XDECREF(completer);
246-
completer = NULL;
247-
tstate = NULL;
248-
}
249-
else if (PyCallable_Check(function)) {
250-
PyObject *tmp = completer;
251-
Py_INCREF(function);
252-
completer = function;
253-
Py_XDECREF(tmp);
254-
tstate = PyThreadState_Get();
255-
}
256-
else {
257-
PyErr_SetString(PyExc_TypeError,
258-
"set_completer(func): argument not callable");
259-
return NULL;
260-
}
261-
Py_INCREF(Py_None);
262-
return Py_None;
309+
return set_hook("completer", &completer, &completer_tstate, args);
263310
}
264311

265312
static char doc_set_completer[] = "\
@@ -330,9 +377,60 @@ static struct PyMethodDef readline_methods[] =
330377
METH_VARARGS, doc_set_completer_delims},
331378
{"get_completer_delims", get_completer_delims,
332379
METH_OLDARGS, doc_get_completer_delims},
380+
381+
{"set_startup_hook", set_startup_hook, METH_VARARGS, doc_set_startup_hook},
382+
#ifdef HAVE_RL_PRE_INPUT_HOOK
383+
{"set_pre_input_hook", set_pre_input_hook, METH_VARARGS, doc_set_pre_input_hook},
384+
#endif
333385
{0, 0}
334386
};
335387

388+
/* C function to call the Python hooks. */
389+
390+
static int
391+
on_hook(PyObject *func, PyThreadState *tstate)
392+
{
393+
int result = 0;
394+
if (func != NULL) {
395+
PyObject *r;
396+
PyThreadState *save_tstate;
397+
/* Note that readline is called with the interpreter
398+
lock released! */
399+
save_tstate = PyThreadState_Swap(NULL);
400+
PyEval_RestoreThread(tstate);
401+
r = PyObject_CallFunction(func, NULL);
402+
if (r == NULL)
403+
goto error;
404+
if (r == Py_None)
405+
result = 0;
406+
else
407+
result = PyInt_AsLong(r);
408+
Py_DECREF(r);
409+
goto done;
410+
error:
411+
PyErr_Clear();
412+
Py_XDECREF(r);
413+
done:
414+
PyEval_SaveThread();
415+
PyThreadState_Swap(save_tstate);
416+
}
417+
return result;
418+
}
419+
420+
static int
421+
on_startup_hook(void)
422+
{
423+
return on_hook(startup_hook, startup_hook_tstate);
424+
}
425+
426+
#ifdef HAVE_RL_PRE_INPUT_HOOK
427+
static int
428+
on_pre_input_hook(void)
429+
{
430+
return on_hook(pre_input_hook, pre_input_hook_tstate);
431+
}
432+
#endif
433+
336434
/* C function to call the Python completer. */
337435

338436
static char *
@@ -345,7 +443,7 @@ on_completion(char *text, int state)
345443
/* Note that readline is called with the interpreter
346444
lock released! */
347445
save_tstate = PyThreadState_Swap(NULL);
348-
PyEval_RestoreThread(tstate);
446+
PyEval_RestoreThread(completer_tstate);
349447
r = PyObject_CallFunction(completer, "si", text, state);
350448
if (r == NULL)
351449
goto error;
@@ -395,6 +493,11 @@ setup_readline(void)
395493
/* Bind both ESC-TAB and ESC-ESC to the completion function */
396494
rl_bind_key_in_map ('\t', rl_complete, emacs_meta_keymap);
397495
rl_bind_key_in_map ('\033', rl_complete, emacs_meta_keymap);
496+
/* Set our hook functions */
497+
rl_startup_hook = (Function *)on_startup_hook;
498+
#ifdef HAVE_RL_PRE_INPUT_HOOK
499+
rl_pre_input_hook = (Function *)on_pre_input_hook;
500+
#endif
398501
/* Set our completion function */
399502
rl_attempted_completion_function = (CPPFunction *)flex_complete;
400503
/* Set Python word break characters */

acconfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@
8787
/* Define if you have GNU PTH threads */
8888
#undef HAVE_PTH
8989

90+
/* Define if you have readline 4.0 */
91+
#undef HAVE_RL_PRE_INPUT_HOOK
92+
9093
/* Define if you have readline 4.2 */
9194
#undef HAVE_RL_COMPLETION_MATCHES
9295

configure

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7027,17 +7027,62 @@ EOF
70277027

70287028
fi
70297029

7030+
# check for readline 4.0
7031+
echo $ac_n "checking for rl_pre_input_hook in -lreadline""... $ac_c" 1>&6
7032+
echo "configure:7033: checking for rl_pre_input_hook in -lreadline" >&5
7033+
ac_lib_var=`echo readline'_'rl_pre_input_hook | sed 'y%./+-%__p_%'`
7034+
if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
7035+
echo $ac_n "(cached) $ac_c" 1>&6
7036+
else
7037+
ac_save_LIBS="$LIBS"
7038+
LIBS="-lreadline -ltermcap $LIBS"
7039+
cat > conftest.$ac_ext <<EOF
7040+
#line 7041 "configure"
7041+
#include "confdefs.h"
7042+
/* Override any gcc2 internal prototype to avoid an error. */
7043+
/* We use char because int might match the return type of a gcc2
7044+
builtin and then its argument prototype would still apply. */
7045+
char rl_pre_input_hook();
7046+
7047+
int main() {
7048+
rl_pre_input_hook()
7049+
; return 0; }
7050+
EOF
7051+
if { (eval echo configure:7052: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
7052+
rm -rf conftest*
7053+
eval "ac_cv_lib_$ac_lib_var=yes"
7054+
else
7055+
echo "configure: failed program was:" >&5
7056+
cat conftest.$ac_ext >&5
7057+
rm -rf conftest*
7058+
eval "ac_cv_lib_$ac_lib_var=no"
7059+
fi
7060+
rm -f conftest*
7061+
LIBS="$ac_save_LIBS"
7062+
7063+
fi
7064+
if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
7065+
echo "$ac_t""yes" 1>&6
7066+
cat >> confdefs.h <<\EOF
7067+
#define HAVE_RL_PRE_INPUT_HOOK 1
7068+
EOF
7069+
7070+
else
7071+
echo "$ac_t""no" 1>&6
7072+
fi
7073+
7074+
70307075
# check for readline 4.2
70317076
echo $ac_n "checking for rl_completion_matches in -lreadline""... $ac_c" 1>&6
7032-
echo "configure:7033: checking for rl_completion_matches in -lreadline" >&5
7077+
echo "configure:7078: checking for rl_completion_matches in -lreadline" >&5
70337078
ac_lib_var=`echo readline'_'rl_completion_matches | sed 'y%./+-%__p_%'`
70347079
if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
70357080
echo $ac_n "(cached) $ac_c" 1>&6
70367081
else
70377082
ac_save_LIBS="$LIBS"
70387083
LIBS="-lreadline -ltermcap $LIBS"
70397084
cat > conftest.$ac_ext <<EOF
7040-
#line 7041 "configure"
7085+
#line 7086 "configure"
70417086
#include "confdefs.h"
70427087
/* Override any gcc2 internal prototype to avoid an error. */
70437088
/* We use char because int might match the return type of a gcc2
@@ -7048,7 +7093,7 @@ int main() {
70487093
rl_completion_matches()
70497094
; return 0; }
70507095
EOF
7051-
if { (eval echo configure:7052: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
7096+
if { (eval echo configure:7097: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
70527097
rm -rf conftest*
70537098
eval "ac_cv_lib_$ac_lib_var=yes"
70547099
else
@@ -7073,7 +7118,7 @@ fi
70737118

70747119

70757120
echo $ac_n "checking for broken nice()""... $ac_c" 1>&6
7076-
echo "configure:7077: checking for broken nice()" >&5
7121+
echo "configure:7122: checking for broken nice()" >&5
70777122
if eval "test \"`echo '$''{'ac_cv_broken_nice'+set}'`\" = set"; then
70787123
echo $ac_n "(cached) $ac_c" 1>&6
70797124
else
@@ -7082,7 +7127,7 @@ if test "$cross_compiling" = yes; then
70827127
ac_cv_broken_nice=no
70837128
else
70847129
cat > conftest.$ac_ext <<EOF
7085-
#line 7086 "configure"
7130+
#line 7131 "configure"
70867131
#include "confdefs.h"
70877132
70887133
int main()
@@ -7094,7 +7139,7 @@ int main()
70947139
}
70957140
70967141
EOF
7097-
if { (eval echo configure:7098: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
7142+
if { (eval echo configure:7143: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
70987143
then
70997144
ac_cv_broken_nice=yes
71007145
else
@@ -7125,12 +7170,12 @@ cat >> confdefs.h <<\EOF
71257170
#endif
71267171
EOF
71277172
echo $ac_n "checking for socklen_t""... $ac_c" 1>&6
7128-
echo "configure:7129: checking for socklen_t" >&5
7173+
echo "configure:7174: checking for socklen_t" >&5
71297174
if eval "test \"`echo '$''{'ac_cv_type_socklen_t'+set}'`\" = set"; then
71307175
echo $ac_n "(cached) $ac_c" 1>&6
71317176
else
71327177
cat > conftest.$ac_ext <<EOF
7133-
#line 7134 "configure"
7178+
#line 7179 "configure"
71347179
#include "confdefs.h"
71357180
#include <sys/types.h>
71367181
#if STDC_HEADERS
@@ -7179,7 +7224,7 @@ done
71797224

71807225
SRCDIRS="Parser Grammar Objects Python Modules"
71817226
echo $ac_n "checking for build directories""... $ac_c" 1>&6
7182-
echo "configure:7183: checking for build directories" >&5
7227+
echo "configure:7228: checking for build directories" >&5
71837228
for dir in $SRCDIRS; do
71847229
if test ! -d $dir; then
71857230
mkdir $dir

configure.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,10 @@ then
18321832
AC_DEFINE(HAVE_GETC_UNLOCKED)
18331833
fi
18341834

1835+
# check for readline 4.0
1836+
AC_CHECK_LIB(readline, rl_pre_input_hook,
1837+
AC_DEFINE(HAVE_RL_PRE_INPUT_HOOK), , -ltermcap)
1838+
18351839
# check for readline 4.2
18361840
AC_CHECK_LIB(readline, rl_completion_matches,
18371841
AC_DEFINE(HAVE_RL_COMPLETION_MATCHES), , -ltermcap)

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@
149149
/* Define if you have GNU PTH threads */
150150
#undef HAVE_PTH
151151

152+
/* Define if you have readline 4.0 */
153+
#undef HAVE_RL_PRE_INPUT_HOOK
154+
152155
/* Define if you have readline 4.2 */
153156
#undef HAVE_RL_COMPLETION_MATCHES
154157

0 commit comments

Comments
 (0)