mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
parent
aacfcccdc3
commit
33f8f15bdd
4 changed files with 106 additions and 2 deletions
|
@ -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. ::
|
||||||
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. ::
|
||||||
|
|
||||||
def save_history(self, histfile):
|
def save_history(self, histfile):
|
||||||
readline.write_history_file(histfile)
|
readline.write_history_file(histfile)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
||||||
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
|
||||||
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\
|
||||||
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[] =
|
||||||
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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue