Commit 33f8f15b authored by Benjamin Peterson's avatar Benjamin Peterson

add readline.append_history_file (closes #22940)

patch by "bru"
parent aacfcccd
...@@ -59,6 +59,14 @@ The :mod:`readline` module defines the following functions: ...@@ -59,6 +59,14 @@ The :mod:`readline` module defines the following functions:
Save a readline history file. The default filename is :file:`~/.history`. 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() .. function:: clear_history()
Clear the current history. (Note: this function is not available if the Clear the current history. (Note: this function is not available if the
...@@ -209,6 +217,26 @@ from the user's :envvar:`PYTHONSTARTUP` file. :: ...@@ -209,6 +217,26 @@ from the user's :envvar:`PYTHONSTARTUP` file. ::
This code is actually automatically run when Python is run in This code is actually automatically run when Python is run in
:ref:`interactive mode <tut-interactive>` (see :ref:`rlcompleter-config`). :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 The following example extends the :class:`code.InteractiveConsole` class to
support history save/restore. :: support history save/restore. ::
...@@ -234,4 +262,3 @@ support history save/restore. :: ...@@ -234,4 +262,3 @@ support history save/restore. ::
def save_history(self, histfile): def save_history(self, histfile):
readline.write_history_file(histfile) readline.write_history_file(histfile)
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
Very minimal unittests for parts of the readline module. Very minimal unittests for parts of the readline module.
""" """
import os import os
import tempfile
import unittest 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 from test.script_helper import assert_python_ok
# Skip tests if there is no readline module # Skip tests if there is no readline module
...@@ -42,6 +43,43 @@ class TestHistoryManipulation (unittest.TestCase): ...@@ -42,6 +43,43 @@ class TestHistoryManipulation (unittest.TestCase):
self.assertEqual(readline.get_current_history_length(), 1) 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): class TestReadline(unittest.TestCase):
......
...@@ -191,6 +191,8 @@ Core and Builtins ...@@ -191,6 +191,8 @@ Core and Builtins
Library Library
------- -------
- Issue #22940: Add readline.append_history_file.
- Issue #19676: Added the "namereplace" error handler. - Issue #19676: Added the "namereplace" error handler.
- Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler. - Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.
......
...@@ -237,6 +237,41 @@ Save a readline history file.\n\ ...@@ -237,6 +237,41 @@ Save a readline history file.\n\
The default filename is ~/.history."); 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 */ /* Set history length */
static PyObject* static PyObject*
...@@ -747,6 +782,8 @@ static struct PyMethodDef readline_methods[] = ...@@ -747,6 +782,8 @@ static struct PyMethodDef readline_methods[] =
METH_VARARGS, doc_read_history_file}, METH_VARARGS, doc_read_history_file},
{"write_history_file", write_history_file, {"write_history_file", write_history_file,
METH_VARARGS, doc_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, {"get_history_item", get_history_item,
METH_VARARGS, doc_get_history_item}, METH_VARARGS, doc_get_history_item},
{"get_current_history_length", (PyCFunction)get_current_history_length, {"get_current_history_length", (PyCFunction)get_current_history_length,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment