bpo-43172: readline now passes its tests when built against libedit (GH-24499)
bpo-43172: readline now passes its tests when built against libedit.
Existing irreconcilable API differences remain in readline.get_begidx
and readline.get_endidx behavior based on libreadline vs libedit use.
A note about that has been documented.
diff --git a/Modules/readline.c b/Modules/readline.c
index 02b2c40..c900e07 100644
--- a/Modules/readline.c
+++ b/Modules/readline.c
@@ -67,7 +67,8 @@ extern char **completion_matches(char *, CPFunction *);
static int using_libedit_emulation = 0;
static const char libedit_version_tag[] = "EditLine wrapper";
-static int libedit_history_start = 0;
+static int8_t libedit_history_start = 0;
+static int8_t libedit_append_replace_history_offset = 0;
#ifdef HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK
static void
@@ -320,7 +321,8 @@ readline_append_history_file_impl(PyObject *module, int nelements,
filename_bytes = NULL;
filename = NULL;
}
- errno = err = append_history(nelements, filename);
+ errno = err = append_history(
+ nelements - libedit_append_replace_history_offset, filename);
if (!err && _history_length >= 0)
history_truncate_file(filename, _history_length);
Py_XDECREF(filename_bytes);
@@ -592,12 +594,12 @@ readline.remove_history_item
pos as entry_number: int
/
-Remove history item given by its position.
+Remove history item given by its zero-based position.
[clinic start generated code]*/
static PyObject *
readline_remove_history_item_impl(PyObject *module, int entry_number)
-/*[clinic end generated code: output=ab114f029208c7e8 input=c8520ac3da50224e]*/
+/*[clinic end generated code: output=ab114f029208c7e8 input=f248beb720ff1838]*/
{
HIST_ENTRY *entry;
@@ -626,12 +628,14 @@ readline.replace_history_item
/
Replaces history item given by its position with contents of line.
+
+pos is zero-based.
[clinic start generated code]*/
static PyObject *
readline_replace_history_item_impl(PyObject *module, int entry_number,
PyObject *line)
-/*[clinic end generated code: output=f8cec2770ca125eb input=b7ccef0780ae041b]*/
+/*[clinic end generated code: output=f8cec2770ca125eb input=368bb66fe5ee5222]*/
{
PyObject *encoded;
HIST_ENTRY *old_entry;
@@ -645,7 +649,9 @@ readline_replace_history_item_impl(PyObject *module, int entry_number,
if (encoded == NULL) {
return NULL;
}
- old_entry = replace_history_entry(entry_number, PyBytes_AS_STRING(encoded), (void *)NULL);
+ old_entry = replace_history_entry(
+ entry_number + libedit_append_replace_history_offset,
+ PyBytes_AS_STRING(encoded), (void *)NULL);
Py_DECREF(encoded);
if (!old_entry) {
PyErr_Format(PyExc_ValueError,
@@ -786,12 +792,12 @@ readline.get_history_item
index as idx: int
/
-Return the current contents of history item at index.
+Return the current contents of history item at one-based index.
[clinic start generated code]*/
static PyObject *
readline_get_history_item_impl(PyObject *module, int idx)
-/*[clinic end generated code: output=83d3e53ea5f34b3d input=63fff0c3c4323269]*/
+/*[clinic end generated code: output=83d3e53ea5f34b3d input=8adf5c80e6c7ff2b]*/
{
HIST_ENTRY *hist_ent;
@@ -1191,6 +1197,22 @@ setup_readline(readlinestate *mod_state)
} else {
libedit_history_start = 1;
}
+ /* Some libedit implementations use 1 based indexing on
+ * replace_history_entry where libreadline uses 0 based.
+ * The API our module presents is supposed to be 0 based.
+ * It's a mad mad mad mad world.
+ */
+ {
+ add_history("2");
+ HIST_ENTRY *old_entry = replace_history_entry(1, "X", NULL);
+ _py_free_history_entry(old_entry);
+ HIST_ENTRY *item = history_get(libedit_history_start);
+ if (item && item->line && strcmp(item->line, "X")) {
+ libedit_append_replace_history_offset = 0;
+ } else {
+ libedit_append_replace_history_offset = 1;
+ }
+ }
clear_history();
using_history();