Skip to content

Commit 9df1fcb

Browse files
authored
Fix history: do not nuke on errors, fix encoding for py3 (#4)
* write_history_file: do not nuke history on errors * Pick write_history_file encoding fix from PyPy (py3.6)
1 parent baf30dd commit 9df1fcb

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

pyrepl/readline.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -308,15 +308,21 @@ def read_history_file(self, filename='~/.history'):
308308
def write_history_file(self, filename='~/.history'):
309309
maxlength = self.saved_history_length
310310
history = self.get_reader().get_trimmed_history(maxlength)
311-
f = open(os.path.expanduser(filename), 'w')
311+
entries = ''
312312
for entry in history:
313-
if isinstance(entry, unicode):
314-
try:
315-
entry = entry.encode(ENCODING)
316-
except UnicodeEncodeError: # bah, silently fall back...
317-
entry = entry.encode('utf-8')
313+
# if we are on py3k, we don't need to encode strings before
314+
# writing it to a file
315+
if isinstance(entry, unicode) and sys.version_info < (3,):
316+
entry = entry.encode('utf-8')
318317
entry = entry.replace('\n', '\r\n') # multiline history support
319-
f.write(entry + '\n')
318+
entries += entry + '\n'
319+
320+
fname = os.path.expanduser(filename)
321+
if PY3:
322+
f = open(fname, 'w', encoding='utf-8')
323+
else:
324+
f = open(fname, 'w')
325+
f.write(entries)
320326
f.close()
321327

322328
def clear_history(self):

testing/test_readline.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,40 @@ def test_read_history_file(readline_wrapper, tmp_path):
6666
histfile.write_bytes(b"foo\nbar\n")
6767
readline_wrapper.read_history_file(str(histfile))
6868
assert readline_wrapper.reader.history == ["foo", "bar"]
69+
70+
71+
def test_write_history_file(readline_wrapper, tmp_path):
72+
histfile = tmp_path / "history"
73+
74+
reader = readline_wrapper.get_reader()
75+
history = reader.history
76+
assert history == []
77+
history.extend(["foo", "bar"])
78+
79+
readline_wrapper.write_history_file(str(histfile))
80+
81+
assert open(str(histfile), "r").readlines() == ["foo\n", "bar\n"]
82+
83+
84+
def test_write_history_file_with_exception(readline_wrapper, tmp_path):
85+
"""The history file should not get nuked on inner exceptions.
86+
87+
This was the case with unicode decoding previously."""
88+
histfile = tmp_path / "history"
89+
histfile.write_bytes(b"foo\nbar\n")
90+
91+
class BadEntryException(Exception):
92+
pass
93+
94+
class BadEntry(object):
95+
@classmethod
96+
def replace(cls, *args):
97+
raise BadEntryException
98+
99+
history = readline_wrapper.get_reader().history
100+
history.extend([BadEntry])
101+
102+
with pytest.raises(BadEntryException):
103+
readline_wrapper.write_history_file(str(histfile))
104+
105+
assert open(str(histfile), "r").readlines() == ["foo\n", "bar\n"]

0 commit comments

Comments
 (0)