Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 33f8f15

Browse files
committed
add readline.append_history_file (closes #22940)
patch by "bru"
1 parent aacfccc commit 33f8f15

4 files changed

Lines changed: 106 additions & 2 deletions

File tree

Doc/library/readline.rst

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ The :mod:`readline` module defines the following functions:
5959
Save a readline history file. The default filename is :file:`~/.history`.
6060

6161

62+
.. function:: append_history_file(nelements[, filename])
63+
64+
Append the last *nelements* of history to a file. The default filename is
65+
:file:`~/.history`. The file must already exist.
66+
67+
.. versionadded:: 3.5
68+
69+
6270
.. function:: clear_history()
6371

6472
Clear the current history. (Note: this function is not available if the
@@ -209,6 +217,26 @@ from the user's :envvar:`PYTHONSTARTUP` file. ::
209217
This code is actually automatically run when Python is run in
210218
:ref:`interactive mode <tut-interactive>` (see :ref:`rlcompleter-config`).
211219

220+
The following example achieves the same goal but supports concurrent interactive
221+
sessions, by only appending the new history. ::
222+
223+
import atexit
224+
import os
225+
import realine
226+
histfile = os.path.join(os.path.expanduser("~"), ".python_history")
227+
228+
try:
229+
readline.read_history_file(histfile)
230+
h_len = readline.get_history_length()
231+
except FileNotFoundError:
232+
open(histfile, 'wb').close()
233+
h_len = 0
234+
235+
def save(prev_h_len, histfile):
236+
new_h_len = readline.get_history_length()
237+
readline.append_history_file(new_h_len - prev_h_len, histfile)
238+
atexit.register(save, h_len, histfile)
239+
212240
The following example extends the :class:`code.InteractiveConsole` class to
213241
support history save/restore. ::
214242

@@ -234,4 +262,3 @@ support history save/restore. ::
234262

235263
def save_history(self, histfile):
236264
readline.write_history_file(histfile)
237-

Lib/test/test_readline.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
Very minimal unittests for parts of the readline module.
33
"""
44
import os
5+
import tempfile
56
import unittest
6-
from test.support import run_unittest, import_module
7+
from test.support import run_unittest, import_module, unlink
78
from test.script_helper import assert_python_ok
89

910
# Skip tests if there is no readline module
@@ -42,6 +43,43 @@ def testHistoryUpdates(self):
4243

4344
self.assertEqual(readline.get_current_history_length(), 1)
4445

46+
def test_write_read_append(self):
47+
hfile = tempfile.NamedTemporaryFile(delete=False)
48+
hfile.close()
49+
hfilename = hfile.name
50+
self.addCleanup(unlink, hfilename)
51+
52+
# test write-clear-read == nop
53+
readline.clear_history()
54+
readline.add_history("first line")
55+
readline.add_history("second line")
56+
readline.write_history_file(hfilename)
57+
58+
readline.clear_history()
59+
self.assertEqual(readline.get_current_history_length(), 0)
60+
61+
readline.read_history_file(hfilename)
62+
self.assertEqual(readline.get_current_history_length(), 2)
63+
self.assertEqual(readline.get_history_item(1), "first line")
64+
self.assertEqual(readline.get_history_item(2), "second line")
65+
66+
# test append
67+
readline.append_history_file(1, hfilename)
68+
readline.clear_history()
69+
readline.read_history_file(hfilename)
70+
self.assertEqual(readline.get_current_history_length(), 3)
71+
self.assertEqual(readline.get_history_item(1), "first line")
72+
self.assertEqual(readline.get_history_item(2), "second line")
73+
self.assertEqual(readline.get_history_item(3), "second line")
74+
75+
# test 'no such file' behaviour
76+
os.unlink(hfilename)
77+
with self.assertRaises(FileNotFoundError):
78+
readline.append_history_file(1, hfilename)
79+
80+
# write_history_file can create the target
81+
readline.write_history_file(hfilename)
82+
4583

4684
class TestReadline(unittest.TestCase):
4785

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ Core and Builtins
191191
Library
192192
-------
193193

194+
- Issue #22940: Add readline.append_history_file.
195+
194196
- Issue #19676: Added the "namereplace" error handler.
195197

196198
- Issue #22788: Add *context* parameter to logging.handlers.HTTPHandler.

Modules/readline.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,41 @@ Save a readline history file.\n\
237237
The default filename is ~/.history.");
238238

239239

240+
/* Exported function to save part of a readline history file */
241+
242+
static PyObject *
243+
append_history_file(PyObject *self, PyObject *args)
244+
{
245+
int nelements;
246+
PyObject *filename_obj = Py_None, *filename_bytes;
247+
char *filename;
248+
int err;
249+
if (!PyArg_ParseTuple(args, "i|O:append_history_file", &nelements, &filename_obj))
250+
return NULL;
251+
if (filename_obj != Py_None) {
252+
if (!PyUnicode_FSConverter(filename_obj, &filename_bytes))
253+
return NULL;
254+
filename = PyBytes_AsString(filename_bytes);
255+
} else {
256+
filename_bytes = NULL;
257+
filename = NULL;
258+
}
259+
errno = err = append_history(nelements, filename);
260+
if (!err && _history_length >= 0)
261+
history_truncate_file(filename, _history_length);
262+
Py_XDECREF(filename_bytes);
263+
errno = err;
264+
if (errno)
265+
return PyErr_SetFromErrno(PyExc_IOError);
266+
Py_RETURN_NONE;
267+
}
268+
269+
PyDoc_STRVAR(doc_append_history_file,
270+
"append_history_file(nelements[, filename]) -> None\n\
271+
Append the last nelements of the history list to file.\n\
272+
The default filename is ~/.history.");
273+
274+
240275
/* Set history length */
241276

242277
static PyObject*
@@ -747,6 +782,8 @@ static struct PyMethodDef readline_methods[] =
747782
METH_VARARGS, doc_read_history_file},
748783
{"write_history_file", write_history_file,
749784
METH_VARARGS, doc_write_history_file},
785+
{"append_history_file", append_history_file,
786+
METH_VARARGS, doc_append_history_file},
750787
{"get_history_item", get_history_item,
751788
METH_VARARGS, doc_get_history_item},
752789
{"get_current_history_length", (PyCFunction)get_current_history_length,

0 commit comments

Comments
 (0)