add readline.append_history_file (closes #22940)

patch by "bru"
This commit is contained in:
Benjamin Peterson 2014-11-26 13:58:16 -06:00
parent aacfcccdc3
commit 33f8f15bdd
4 changed files with 106 additions and 2 deletions

View 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)

View 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):

View File

@ -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.

View 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,