]> granicus.if.org Git - python/commitdiff
add readline.append_history_file (closes #22940)
authorBenjamin Peterson <benjamin@python.org>
Wed, 26 Nov 2014 19:58:16 +0000 (13:58 -0600)
committerBenjamin Peterson <benjamin@python.org>
Wed, 26 Nov 2014 19:58:16 +0000 (13:58 -0600)
patch by "bru"

Doc/library/readline.rst
Lib/test/test_readline.py
Misc/NEWS
Modules/readline.c

index 692310b941dcaf815c7953510fd8bac0ec8c5181..3864f0d22506f7a2b1d847154aab0380f7066aae 100644 (file)
@@ -59,6 +59,14 @@ The :mod:`readline` module defines the following functions:
    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 @@ from the user's :envvar:`PYTHONSTARTUP` file. ::
 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 @@ support history save/restore. ::
 
        def save_history(self, histfile):
            readline.write_history_file(histfile)
-
index d2a11f2d0222b14e1531c7ad16447bc64778c85a..e5be02eb8029e534b5eee72f29622f0107b43a8e 100644 (file)
@@ -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 @@ class TestHistoryManipulation (unittest.TestCase):
 
         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):
 
index 2662e6019ff79aba29c74df9ba32419625998141..75260e01c069a21223af8184e6a096a235548d2f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -191,6 +191,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #22940: Add readline.append_history_file.
+
 - Issue #19676: Added the "namereplace" error handler.
 
 - Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
index f349d3b109df29640fd93285ebba9e29a1401cf1..b5a0eba50bc4b6a7e3412506b4c2839372f68f30 100644 (file)
@@ -237,6 +237,41 @@ Save a readline history file.\n\
 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 @@ static struct PyMethodDef readline_methods[] =
      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,