Issue 6877: this patch makes it possible to link the readline extension
to the libedit emulation of the readline API on OSX 10.5 or later. This also adds a minimal testsuite for readline to check that the history manipuation functions have the same interface with both C libraries.
This commit is contained in:
parent
956f4b2520
commit
9f20d9d0ee
|
@ -14,6 +14,17 @@ made using this module affect the behaviour of both the interpreter's
|
||||||
interactive prompt and the prompts offered by the :func:`raw_input` and
|
interactive prompt and the prompts offered by the :func:`raw_input` and
|
||||||
:func:`input` built-in functions.
|
:func:`input` built-in functions.
|
||||||
|
|
||||||
|
..note::
|
||||||
|
|
||||||
|
On MacOS X the :mod:`readline` module can be implemented using
|
||||||
|
the ``libedit`` library instead of GNU readline.
|
||||||
|
|
||||||
|
The configuration file for ``libedit`` is different from that
|
||||||
|
of GNU readline. If you programmaticly load configuration strings
|
||||||
|
you can check for the text "libedit" in :const:`readline.__doc__`
|
||||||
|
to differentiate between GNU readline and libedit.
|
||||||
|
|
||||||
|
|
||||||
The :mod:`readline` module defines the following functions:
|
The :mod:`readline` module defines the following functions:
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,7 +192,6 @@ The :mod:`readline` module defines the following functions:
|
||||||
|
|
||||||
Append a line to the history buffer, as if it was the last line typed.
|
Append a line to the history buffer, as if it was the last line typed.
|
||||||
|
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
Module :mod:`rlcompleter`
|
Module :mod:`rlcompleter`
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""
|
||||||
|
Very minimal unittests for parts of the readline module.
|
||||||
|
|
||||||
|
These tests were added to check that the libedit emulation on OSX and
|
||||||
|
the "real" readline have the same interface for history manipulation. That's
|
||||||
|
why the tests cover only a small subset of the interface.
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
from test.test_support import run_unittest
|
||||||
|
|
||||||
|
import readline
|
||||||
|
|
||||||
|
class TestHistoryManipulation (unittest.TestCase):
|
||||||
|
def testHistoryUpdates(self):
|
||||||
|
readline.clear_history()
|
||||||
|
|
||||||
|
readline.add_history("first line")
|
||||||
|
readline.add_history("second line")
|
||||||
|
|
||||||
|
self.assertEqual(readline.get_history_item(0), None)
|
||||||
|
self.assertEqual(readline.get_history_item(1), "first line")
|
||||||
|
self.assertEqual(readline.get_history_item(2), "second line")
|
||||||
|
|
||||||
|
readline.replace_history_item(0, "replaced line")
|
||||||
|
self.assertEqual(readline.get_history_item(0), None)
|
||||||
|
self.assertEqual(readline.get_history_item(1), "replaced line")
|
||||||
|
self.assertEqual(readline.get_history_item(2), "second line")
|
||||||
|
|
||||||
|
self.assertEqual(readline.get_current_history_length(), 2)
|
||||||
|
|
||||||
|
readline.remove_history_item(0)
|
||||||
|
self.assertEqual(readline.get_history_item(0), None)
|
||||||
|
self.assertEqual(readline.get_history_item(1), "second line")
|
||||||
|
|
||||||
|
self.assertEqual(readline.get_current_history_length(), 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_main():
|
||||||
|
run_unittest(TestHistoryManipulation)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_main()
|
|
@ -1335,6 +1335,9 @@ C-API
|
||||||
Extension Modules
|
Extension Modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #6877: Make it possible to link the readline extension to libedit
|
||||||
|
on OSX.
|
||||||
|
|
||||||
- Issue #6944: Fix a SystemError when socket.getnameinfo() was called
|
- Issue #6944: Fix a SystemError when socket.getnameinfo() was called
|
||||||
with something other than a tuple as first argument.
|
with something other than a tuple as first argument.
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,25 @@ extern char **completion_matches(char *, CPFunction *);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
/*
|
||||||
|
* It is possible to link the readline module to the readline
|
||||||
|
* emulation library of editline/libedit.
|
||||||
|
*
|
||||||
|
* On OSX this emulation library is not 100% API compatible
|
||||||
|
* with the "real" readline and cannot be detected at compile-time,
|
||||||
|
* hence we use a runtime check to detect if we're using libedit
|
||||||
|
*
|
||||||
|
* Currently there is one know API incompatibility:
|
||||||
|
* - 'get_history' has a 1-based index with GNU readline, and a 0-based
|
||||||
|
* index with libedit's emulation.
|
||||||
|
* - Note that replace_history and remove_history use a 0-based index
|
||||||
|
* with both implementation.
|
||||||
|
*/
|
||||||
|
static int using_libedit_emulation = 0;
|
||||||
|
static const char libedit_version_tag[] = "EditLine wrapper";
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_completion_display_matches_hook(char **matches,
|
on_completion_display_matches_hook(char **matches,
|
||||||
int num_matches, int max_length);
|
int num_matches, int max_length);
|
||||||
|
@ -478,6 +497,29 @@ get_history_item(PyObject *self, PyObject *args)
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "i:index", &idx))
|
if (!PyArg_ParseTuple(args, "i:index", &idx))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (using_libedit_emulation) {
|
||||||
|
/* Libedit emulation uses 0-based indexes,
|
||||||
|
* the real one uses 1-based indexes,
|
||||||
|
* adjust the index to ensure that Python
|
||||||
|
* code doesn't have to worry about the
|
||||||
|
* difference.
|
||||||
|
*/
|
||||||
|
HISTORY_STATE *hist_st;
|
||||||
|
hist_st = history_get_history_state();
|
||||||
|
|
||||||
|
idx --;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apple's readline emulation crashes when
|
||||||
|
* the index is out of range, therefore
|
||||||
|
* test for that and fail gracefully.
|
||||||
|
*/
|
||||||
|
if (idx < 0 || idx >= hist_st->length) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* __APPLE__ */
|
||||||
if ((hist_ent = history_get(idx)))
|
if ((hist_ent = history_get(idx)))
|
||||||
return PyString_FromString(hist_ent->line);
|
return PyString_FromString(hist_ent->line);
|
||||||
else {
|
else {
|
||||||
|
@ -978,6 +1020,15 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
|
||||||
char *line;
|
char *line;
|
||||||
HISTORY_STATE *state = history_get_history_state();
|
HISTORY_STATE *state = history_get_history_state();
|
||||||
if (state->length > 0)
|
if (state->length > 0)
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (using_libedit_emulation) {
|
||||||
|
/*
|
||||||
|
* Libedit's emulation uses 0-based indexes,
|
||||||
|
* the real readline uses 1-based indexes.
|
||||||
|
*/
|
||||||
|
line = history_get(state->length - 1)->line;
|
||||||
|
} else
|
||||||
|
#endif /* __APPLE__ */
|
||||||
line = history_get(state->length)->line;
|
line = history_get(state->length)->line;
|
||||||
else
|
else
|
||||||
line = "";
|
line = "";
|
||||||
|
@ -1011,16 +1062,35 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
|
||||||
PyDoc_STRVAR(doc_module,
|
PyDoc_STRVAR(doc_module,
|
||||||
"Importing this module enables command line editing using GNU readline.");
|
"Importing this module enables command line editing using GNU readline.");
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
PyDoc_STRVAR(doc_module_le,
|
||||||
|
"Importing this module enables command line editing using libedit readline.");
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
PyMODINIT_FUNC
|
||||||
initreadline(void)
|
initreadline(void)
|
||||||
{
|
{
|
||||||
PyObject *m;
|
PyObject *m;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (strncmp(rl_library_version, libedit_version_tag, strlen(libedit_version_tag)) == 0) {
|
||||||
|
using_libedit_emulation = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (using_libedit_emulation)
|
||||||
|
m = Py_InitModule4("readline", readline_methods, doc_module_le,
|
||||||
|
(PyObject *)NULL, PYTHON_API_VERSION);
|
||||||
|
else
|
||||||
|
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
m = Py_InitModule4("readline", readline_methods, doc_module,
|
m = Py_InitModule4("readline", readline_methods, doc_module,
|
||||||
(PyObject *)NULL, PYTHON_API_VERSION);
|
(PyObject *)NULL, PYTHON_API_VERSION);
|
||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PyOS_ReadlineFunctionPointer = call_readline;
|
PyOS_ReadlineFunctionPointer = call_readline;
|
||||||
setup_readline();
|
setup_readline();
|
||||||
}
|
}
|
||||||
|
|
8
setup.py
8
setup.py
|
@ -546,16 +546,16 @@ class PyBuildExt(build_ext):
|
||||||
|
|
||||||
# readline
|
# readline
|
||||||
do_readline = self.compiler_obj.find_library_file(lib_dirs, 'readline')
|
do_readline = self.compiler_obj.find_library_file(lib_dirs, 'readline')
|
||||||
if platform == 'darwin': # and os.uname()[2] < '9.':
|
if platform == 'darwin':
|
||||||
|
os_release = int(os.uname()[2].split('.')[0])
|
||||||
|
if os_release < 9:
|
||||||
# MacOSX 10.4 has a broken readline. Don't try to build
|
# MacOSX 10.4 has a broken readline. Don't try to build
|
||||||
# the readline module unless the user has installed a fixed
|
# the readline module unless the user has installed a fixed
|
||||||
# readline package
|
# readline package
|
||||||
# FIXME: The readline emulation on 10.5 is better, but the
|
|
||||||
# readline module doesn't compile out of the box.
|
|
||||||
if find_file('readline/rlconf.h', inc_dirs, []) is None:
|
if find_file('readline/rlconf.h', inc_dirs, []) is None:
|
||||||
do_readline = False
|
do_readline = False
|
||||||
if do_readline:
|
if do_readline:
|
||||||
if sys.platform == 'darwin':
|
if platform == 'darwin' and os_release < 9:
|
||||||
# In every directory on the search path search for a dynamic
|
# In every directory on the search path search for a dynamic
|
||||||
# library and then a static library, instead of first looking
|
# library and then a static library, instead of first looking
|
||||||
# for dynamic libraries on the entiry path.
|
# for dynamic libraries on the entiry path.
|
||||||
|
|
Loading…
Reference in New Issue