From f0dbf7a6abc821c77e912deae81e3a27cf334c54 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sun, 15 May 2016 01:26:25 +0000 Subject: [PATCH] Issue #26870: Add readline.set_auto_history(), originally by Tyler Crompton --- Doc/library/readline.rst | 14 +++++++++++ Doc/whatsnew/3.6.rst | 8 +++++++ Lib/test/test_readline.py | 49 +++++++++++++++++++++++++++++++++++++++ Misc/ACKS | 1 + Misc/NEWS | 4 ++++ Modules/readline.c | 21 ++++++++++++++++- 6 files changed, 96 insertions(+), 1 deletion(-) diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index 42e0ad56a5e..f77792030f9 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -156,6 +156,20 @@ The following functions operate on a global history list: This calls :c:func:`add_history` in the underlying library. +.. function:: set_auto_history(enabled) + + Enable or disable automatic calls to :c:func:`add_history` when reading + input via readline. The *enabled* argument should be a Boolean value + that when true, enables auto history, and that when False, disables + auto history. + + .. versionadded:: 3.6 + + .. impl-detail:: + Auto history is enabled by default, and changes to this do not persist + across multiple sessions. + + Startup hooks ------------- diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 384e35a4caf..bad0f9e5cfe 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -239,6 +239,14 @@ Protocol version 4 already supports this case. (Contributed by Serhiy Storchaka in :issue:`24164`.) +readline +-------- + +Added :func:`~readline.set_auto_history` to enable or disable +automatic addition of input to the history list. (Contributed by +Tyler Crompton in :issue:`26870`.) + + rlcompleter ----------- diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 35330ab0761..84fd119e6a1 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -1,7 +1,11 @@ """ Very minimal unittests for parts of the readline module. """ +from errno import EIO import os +import selectors +import subprocess +import sys import tempfile import unittest from test.support import import_module, unlink @@ -96,6 +100,51 @@ class TestReadline(unittest.TestCase): TERM='xterm-256color') self.assertEqual(stdout, b'') + auto_history_script = """\ +import readline +readline.set_auto_history({}) +input() +print("History length:", readline.get_current_history_length()) +""" + + def test_auto_history_enabled(self): + output = run_pty(self.auto_history_script.format(True)) + self.assertIn(b"History length: 1\r\n", output) + + def test_auto_history_disabled(self): + output = run_pty(self.auto_history_script.format(False)) + self.assertIn(b"History length: 0\r\n", output) + + +def run_pty(script, input=b"dummy input\r"): + pty = import_module('pty') + output = bytearray() + [master, slave] = pty.openpty() + args = (sys.executable, '-c', script) + proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave) + os.close(slave) + with proc, selectors.DefaultSelector() as sel: + sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE) + os.set_blocking(master, False) + while True: + for [_, events] in sel.select(): + if events & selectors.EVENT_READ: + try: + chunk = os.read(master, 0x10000) + except OSError as err: + # Linux raises EIO when the slave is closed + if err.errno != EIO: + raise + chunk = b"" + if not chunk: + os.close(master) + return output + output.extend(chunk) + if events & selectors.EVENT_WRITE: + input = input[os.write(master, input):] + if not input: + sel.modify(master, selectors.EVENT_READ) + if __name__ == "__main__": unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index ebc3fc61465..65d70402a1f 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -307,6 +307,7 @@ Ryan Coyner Christopher A. Craig Jeremy Craven Laura Creighton +Tyler Crompton Simon Cross Felipe Cruz Drew Csillag diff --git a/Misc/NEWS b/Misc/NEWS index bede515d9ba..80996bdcc1d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -277,6 +277,10 @@ Core and Builtins Library ------- +- Issue #26870: Added readline.set_auto_history(), which can stop entries + being automatically added to the history list. Based on patch by Tyler + Crompton. + - Issue #26039: zipfile.ZipFile.open() can now be used to write data into a ZIP file, as well as for extracting data. Patch by Thomas Kluyver. diff --git a/Modules/readline.c b/Modules/readline.c index a323b696228..de1cc17d18c 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -575,6 +575,24 @@ PyDoc_STRVAR(doc_add_history, "add_history(string) -> None\n\ add an item to the history buffer"); +static int should_auto_add_history = 1; + +/* Enable or disable automatic history */ + +static PyObject * +py_set_auto_history(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "p:set_auto_history", + &should_auto_add_history)) { + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(doc_set_auto_history, +"set_auto_history(enabled) -> None\n\ +Enables or disables automatic history."); + /* Get the tab-completion word-delimiters that readline uses */ @@ -791,6 +809,7 @@ static struct PyMethodDef readline_methods[] = {"set_completer_delims", set_completer_delims, METH_VARARGS, doc_set_completer_delims}, + {"set_auto_history", py_set_auto_history, METH_VARARGS, doc_set_auto_history}, {"add_history", py_add_history, METH_VARARGS, doc_add_history}, {"remove_history_item", py_remove_history, METH_VARARGS, doc_remove_history}, {"replace_history_item", py_replace_history, METH_VARARGS, doc_replace_history}, @@ -1266,7 +1285,7 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) /* we have a valid line */ n = strlen(p); - if (n > 0) { + if (should_auto_add_history && n > 0) { const char *line; int length = _py_get_history_length(); if (length > 0)