From 2efd9247549064887b0206d4a57d2abda03384e8 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 20 Sep 2009 14:53:22 +0000 Subject: Merged revisions 74970 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74970 | ronald.oussoren | 2009-09-20 16:18:15 +0200 (Sun, 20 Sep 2009) | 7 lines 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. ........ --- Doc/library/readline.rst | 12 ++++++++- Lib/test/test_readline.py | 42 +++++++++++++++++++++++++++++ Misc/NEWS | 2 ++ Modules/readline.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 18 ++++++------- 5 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 Lib/test/test_readline.py diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index b59f680..ab4a099 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -14,6 +14,17 @@ made using this module affect the behaviour of both the interpreter's interactive prompt and the prompts offered by the built-in :func:`input` function. +..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: @@ -166,7 +177,6 @@ The :mod:`readline` module defines the following functions: Append a line to the history buffer, as if it was the last line typed. - .. seealso:: Module :mod:`rlcompleter` diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py new file mode 100644 index 0000000..b2495a3 --- /dev/null +++ b/Lib/test/test_readline.py @@ -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.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() diff --git a/Misc/NEWS b/Misc/NEWS index 3c4c67b..528b3fd 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -194,6 +194,8 @@ Library Extension Modules ----------------- +- Issue #6877: It is now possible to link the readline extension to the + libedit readline emulation on OSX 10.5 or later. - Issue #6848: Fix curses module build failure on OS X 10.6. diff --git a/Modules/readline.c b/Modules/readline.c index 8925d8d..03c95e3 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -42,6 +42,25 @@ extern char **completion_matches(char *, CPFunction *); #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 on_completion_display_matches_hook(char **matches, int num_matches, int max_length); @@ -478,6 +497,29 @@ get_history_item(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "i:index", &idx)) 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))) return PyUnicode_FromString(hist_ent->line); else { @@ -977,6 +1019,15 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) char *line; HISTORY_STATE *state = history_get_history_state(); 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; else line = ""; @@ -1010,6 +1061,10 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) PyDoc_STRVAR(doc_module, "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__ */ static struct PyModuleDef readlinemodule = { PyModuleDef_HEAD_INIT, @@ -1023,15 +1078,29 @@ static struct PyModuleDef readlinemodule = { NULL }; + PyMODINIT_FUNC PyInit_readline(void) { 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) + readlinemodule.m_doc = doc_module_le; + +#endif /* __APPLE__ */ + m = PyModule_Create(&readlinemodule); + if (m == NULL) return NULL; + + PyOS_ReadlineFunctionPointer = call_readline; setup_readline(); return m; diff --git a/setup.py b/setup.py index a6aff54..ebe2344 100644 --- a/setup.py +++ b/setup.py @@ -493,16 +493,16 @@ class PyBuildExt(build_ext): # readline do_readline = self.compiler_obj.find_library_file(lib_dirs, 'readline') - if platform == 'darwin': # and os.uname()[2] < '9.': - # MacOSX 10.4 has a broken readline. Don't try to build - # the readline module unless the user has installed a fixed - # 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: - do_readline = False + 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 + # the readline module unless the user has installed a fixed + # readline package + if find_file('readline/rlconf.h', inc_dirs, []) is None: + do_readline = False 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 # library and then a static library, instead of first looking # for dynamic libraries on the entire path. -- cgit v0.12