bpo-38634: Allow non-apple build to cope with libedit (GH-16986)

The readline module now detects if Python is linked to libedit at runtime
on all platforms.  Previously, the check was only done on macOS.

If Python is used as a library by a binary linking to libedit, the linker
resolves the rl_initialize symbol required by the readline module against
libedit instead of libreadline, which leads to a segfault.

Take advantage of the existing supporting code to have readline module being
compatible with both situations.
This commit is contained in:
serge-sans-paille 2019-12-04 17:02:57 +01:00 committed by Victor Stinner
parent ac0e1c2694
commit 7105319ada
2 changed files with 6 additions and 19 deletions

View File

@ -0,0 +1,2 @@
The :mod:`readline` module now detects if Python is linked to libedit at runtime
on all platforms. Previously, the check was only done on macOS.

View File

@ -45,14 +45,14 @@ 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
* 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 the Python readlinke module is
* linked to libedit.
*
* Currently there is one known API incompatibility:
* - 'get_history' has a 1-based index with GNU readline, and a 0-based
@ -64,7 +64,6 @@ static int using_libedit_emulation = 0;
static const char libedit_version_tag[] = "EditLine wrapper";
static int libedit_history_start = 0;
#endif /* __APPLE__ */
#ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK
static void
@ -693,7 +692,6 @@ get_history_item(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "i:get_history_item", &idx))
return NULL;
#ifdef __APPLE__
if (using_libedit_emulation) {
/* Older versions of libedit's readline emulation
* use 0-based indexes, while readline and newer
@ -713,7 +711,6 @@ get_history_item(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
}
#endif /* __APPLE__ */
if ((hist_ent = history_get(idx)))
return decode(hist_ent->line);
else {
@ -1081,7 +1078,6 @@ setup_readline(readlinestate *mod_state)
/* The name must be defined before initialization */
rl_readline_name = "python";
#ifdef __APPLE__
/* the libedit readline emulation resets key bindings etc
* when calling rl_initialize. So call it upfront
*/
@ -1098,7 +1094,6 @@ setup_readline(readlinestate *mod_state)
libedit_history_start = 1;
}
clear_history();
#endif /* __APPLE__ */
using_history();
@ -1127,9 +1122,7 @@ setup_readline(readlinestate *mod_state)
mod_state->begidx = PyLong_FromLong(0L);
mod_state->endidx = PyLong_FromLong(0L);
#ifdef __APPLE__
if (!using_libedit_emulation)
#endif
{
if (!isatty(STDOUT_FILENO)) {
/* Issue #19884: stdout is not a terminal. Disable meta modifier
@ -1149,11 +1142,9 @@ setup_readline(readlinestate *mod_state)
* XXX: A bug in the readline-2.2 library causes a memory leak
* inside this function. Nothing we can do about it.
*/
#ifdef __APPLE__
if (using_libedit_emulation)
rl_read_init_file(NULL);
else
#endif /* __APPLE__ */
rl_initialize();
RESTORE_LOCALE(saved_locale)
@ -1283,12 +1274,10 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
int length = _py_get_history_length();
if (length > 0) {
HIST_ENTRY *hist_ent;
#ifdef __APPLE__
if (using_libedit_emulation) {
/* handle older 0-based or newer 1-based indexing */
hist_ent = history_get(length + libedit_history_start - 1);
} else
#endif /* __APPLE__ */
hist_ent = history_get(length);
line = hist_ent ? hist_ent->line : "";
} else
@ -1316,10 +1305,8 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const 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,
@ -1340,7 +1327,6 @@ PyInit_readline(void)
PyObject *m;
readlinestate *mod_state;
#ifdef __APPLE__
if (strncmp(rl_library_version, libedit_version_tag, strlen(libedit_version_tag)) == 0) {
using_libedit_emulation = 1;
}
@ -1348,7 +1334,6 @@ PyInit_readline(void)
if (using_libedit_emulation)
readlinemodule.m_doc = doc_module_le;
#endif /* __APPLE__ */
m = PyModule_Create(&readlinemodule);