[Python-checkins] cpython: add readline.append_history_file (closes #22940)

benjamin.peterson python-checkins at python.org
Wed Nov 26 20:58:41 CET 2014


https://hg.python.org/cpython/rev/ff00588791be
changeset:   93601:ff00588791be
parent:      93597:21d1571c0533
user:        Benjamin Peterson <benjamin at python.org>
date:        Wed Nov 26 13:58:16 2014 -0600
summary:
  add readline.append_history_file (closes #22940)

patch by "bru"

files:
  Doc/library/readline.rst  |  29 ++++++++++++++++++-
  Lib/test/test_readline.py |  40 ++++++++++++++++++++++++++-
  Misc/NEWS                 |   2 +
  Modules/readline.c        |  37 ++++++++++++++++++++++++
  4 files changed, 106 insertions(+), 2 deletions(-)


diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst
--- a/Doc/library/readline.rst
+++ b/Doc/library/readline.rst
@@ -59,6 +59,14 @@
    Save a readline history file. The default filename is :file:`~/.history`.
 
 
+.. function:: append_history_file(nelements[, filename])
+
+   Append the last *nelements* of history to a file.  The default filename is
+   :file:`~/.history`.  The file must already exist.
+
+   .. versionadded:: 3.5
+
+
 .. function:: clear_history()
 
    Clear the current history.  (Note: this function is not available if the
@@ -209,6 +217,26 @@
 This code is actually automatically run when Python is run in
 :ref:`interactive mode <tut-interactive>` (see :ref:`rlcompleter-config`).
 
+The following example achieves the same goal but supports concurrent interactive
+sessions, by only appending the new history. ::
+
+   import atexit
+   import os
+   import realine
+   histfile = os.path.join(os.path.expanduser("~"), ".python_history")
+
+   try:
+       readline.read_history_file(histfile)
+       h_len = readline.get_history_length()
+   except FileNotFoundError:
+       open(histfile, 'wb').close()
+       h_len = 0
+
+   def save(prev_h_len, histfile):
+       new_h_len = readline.get_history_length()
+       readline.append_history_file(new_h_len - prev_h_len, histfile)
+   atexit.register(save, h_len, histfile)
+
 The following example extends the :class:`code.InteractiveConsole` class to
 support history save/restore. ::
 
@@ -234,4 +262,3 @@
 
        def save_history(self, histfile):
            readline.write_history_file(histfile)
-
diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py
--- a/Lib/test/test_readline.py
+++ b/Lib/test/test_readline.py
@@ -2,8 +2,9 @@
 Very minimal unittests for parts of the readline module.
 """
 import os
+import tempfile
 import unittest
-from test.support import run_unittest, import_module
+from test.support import run_unittest, import_module, unlink
 from test.script_helper import assert_python_ok
 
 # Skip tests if there is no readline module
@@ -42,6 +43,43 @@
 
         self.assertEqual(readline.get_current_history_length(), 1)
 
+    def test_write_read_append(self):
+        hfile = tempfile.NamedTemporaryFile(delete=False)
+        hfile.close()
+        hfilename = hfile.name
+        self.addCleanup(unlink, hfilename)
+
+        # test write-clear-read == nop
+        readline.clear_history()
+        readline.add_history("first line")
+        readline.add_history("second line")
+        readline.write_history_file(hfilename)
+
+        readline.clear_history()
+        self.assertEqual(readline.get_current_history_length(), 0)
+
+        readline.read_history_file(hfilename)
+        self.assertEqual(readline.get_current_history_length(), 2)
+        self.assertEqual(readline.get_history_item(1), "first line")
+        self.assertEqual(readline.get_history_item(2), "second line")
+
+        # test append
+        readline.append_history_file(1, hfilename)
+        readline.clear_history()
+        readline.read_history_file(hfilename)
+        self.assertEqual(readline.get_current_history_length(), 3)
+        self.assertEqual(readline.get_history_item(1), "first line")
+        self.assertEqual(readline.get_history_item(2), "second line")
+        self.assertEqual(readline.get_history_item(3), "second line")
+
+        # test 'no such file' behaviour
+        os.unlink(hfilename)
+        with self.assertRaises(FileNotFoundError):
+            readline.append_history_file(1, hfilename)
+
+        # write_history_file can create the target
+        readline.write_history_file(hfilename)
+
 
 class TestReadline(unittest.TestCase):
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -191,6 +191,8 @@
 Library
 -------
 
+- Issue #22940: Add readline.append_history_file.
+
 - Issue #19676: Added the "namereplace" error handler.
 
 - Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
diff --git a/Modules/readline.c b/Modules/readline.c
--- a/Modules/readline.c
+++ b/Modules/readline.c
@@ -237,6 +237,41 @@
 The default filename is ~/.history.");
 
 
+/* Exported function to save part of a readline history file */
+
+static PyObject *
+append_history_file(PyObject *self, PyObject *args)
+{
+    int nelements;
+    PyObject *filename_obj = Py_None, *filename_bytes;
+    char *filename;
+    int err;
+    if (!PyArg_ParseTuple(args, "i|O:append_history_file", &nelements, &filename_obj))
+        return NULL;
+    if (filename_obj != Py_None) {
+        if (!PyUnicode_FSConverter(filename_obj, &filename_bytes))
+            return NULL;
+        filename = PyBytes_AsString(filename_bytes);
+    } else {
+        filename_bytes = NULL;
+        filename = NULL;
+    }
+    errno = err = append_history(nelements, filename);
+    if (!err && _history_length >= 0)
+        history_truncate_file(filename, _history_length);
+    Py_XDECREF(filename_bytes);
+    errno = err;
+    if (errno)
+        return PyErr_SetFromErrno(PyExc_IOError);
+    Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(doc_append_history_file,
+"append_history_file(nelements[, filename]) -> None\n\
+Append the last nelements of the history list to file.\n\
+The default filename is ~/.history.");
+
+
 /* Set history length */
 
 static PyObject*
@@ -747,6 +782,8 @@
      METH_VARARGS, doc_read_history_file},
     {"write_history_file", write_history_file,
      METH_VARARGS, doc_write_history_file},
+    {"append_history_file", append_history_file,
+     METH_VARARGS, doc_append_history_file},
     {"get_history_item", get_history_item,
      METH_VARARGS, doc_get_history_item},
     {"get_current_history_length", (PyCFunction)get_current_history_length,

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list