summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--cmd2/cmd2.py29
-rw-r--r--tests/test_cmd2.py38
-rw-r--r--tests/test_transcript.py26
4 files changed, 86 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08c4e15e..5c5f3b1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.8 (TBD, 2018)
+* Bug Fixes
+ * Prevent crashes that could occur attempting to open a file in non-existent directory or with very long filename
+
## 0.9.1 (May 28, 2018)
* Bug Fixes
* fix packaging error for 0.8.x versions (yes we had to deploy a new version
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 94773794..bb4ac4ad 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -1710,7 +1710,7 @@ class Cmd(cmd.Cmd):
if self.timing:
self.pfeedback('Elapsed: %s' % str(datetime.datetime.now() - timestart))
finally:
- if self.allow_redirection:
+ if self.allow_redirection and self.redirecting:
self._restore_output(statement)
except EmptyStatement:
pass
@@ -1854,7 +1854,11 @@ class Cmd(cmd.Cmd):
# REDIRECTION_APPEND or REDIRECTION_OUTPUT
if statement.output == constants.REDIRECTION_APPEND:
mode = 'a'
- sys.stdout = self.stdout = open(os.path.expanduser(statement.output_to), mode)
+ try:
+ sys.stdout = self.stdout = open(os.path.expanduser(statement.output_to), mode)
+ except OSError as ex:
+ self.perror('Not Redirecting because - {}'.format(ex), traceback_war=False)
+ self.redirecting = False
else:
# going to a paste buffer
sys.stdout = self.stdout = tempfile.TemporaryFile(mode="w+")
@@ -2892,16 +2896,19 @@ a..b, a:b, a:, ..b items by indices (inclusive)
self.echo = saved_echo
# finally, we can write the transcript out to the file
- with open(transcript_file, 'w') as fout:
- fout.write(transcript)
-
- # and let the user know what we did
- if len(history) > 1:
- plural = 'commands and their outputs'
+ try:
+ with open(transcript_file, 'w') as fout:
+ fout.write(transcript)
+ except OSError as ex:
+ self.perror('Failed to save transcript: {}'.format(ex), traceback_war=False)
else:
- plural = 'command and its output'
- msg = '{} {} saved to transcript file {!r}'
- self.pfeedback(msg.format(len(history), plural, transcript_file))
+ # and let the user know what we did
+ if len(history) > 1:
+ plural = 'commands and their outputs'
+ else:
+ plural = 'command and its output'
+ msg = '{} {} saved to transcript file {!r}'
+ self.pfeedback(msg.format(len(history), plural, transcript_file))
@with_argument_list
def do_edit(self, arglist):
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 24a14ea2..37592da1 100644
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -615,6 +615,44 @@ def test_output_redirection(base_app):
finally:
os.remove(filename)
+def test_output_redirection_to_nonexistent_directory(base_app):
+ filename = '~/fakedir/this_does_not_exist.txt'
+
+ # Verify that writing to a file in a non-existent directory doesn't work
+ run_cmd(base_app, 'help > {}'.format(filename))
+ expected = normalize(BASE_HELP)
+ with pytest.raises(FileNotFoundError):
+ with open(filename) as f:
+ content = normalize(f.read())
+ assert content == expected
+
+ # Verify that appending to a file also works
+ run_cmd(base_app, 'help history >> {}'.format(filename))
+ expected = normalize(BASE_HELP + '\n' + HELP_HISTORY)
+ with pytest.raises(FileNotFoundError):
+ with open(filename) as f:
+ content = normalize(f.read())
+ assert content == expected
+
+def test_output_redirection_to_too_long_filename(base_app):
+ filename = '~/sdkfhksdjfhkjdshfkjsdhfkjsdhfkjdshfkjdshfkjshdfkhdsfkjhewfuihewiufhweiufhiweufhiuewhiuewhfiuwehfiuewhfiuewhfiuewhfiuewhiuewhfiuewhfiuewfhiuwehewiufhewiuhfiweuhfiuwehfiuewfhiuwehiuewfhiuewhiewuhfiuewhfiuwefhewiuhewiufhewiufhewiufhewiufhewiufhewiufhewiufhewiuhewiufhewiufhewiuheiufhiuewheiwufhewiufheiufheiufhieuwhfewiuhfeiufhiuewfhiuewheiwuhfiuewhfiuewhfeiuwfhewiufhiuewhiuewhfeiuwhfiuwehfuiwehfiuehiuewhfieuwfhieufhiuewhfeiuwfhiuefhueiwhfw'
+
+ # Verify that writing to a file in a non-existent directory doesn't work
+ run_cmd(base_app, 'help > {}'.format(filename))
+ expected = normalize(BASE_HELP)
+ with pytest.raises(OSError):
+ with open(filename) as f:
+ content = normalize(f.read())
+ assert content == expected
+
+ # Verify that appending to a file also works
+ run_cmd(base_app, 'help history >> {}'.format(filename))
+ expected = normalize(BASE_HELP + '\n' + HELP_HISTORY)
+ with pytest.raises(OSError):
+ with open(filename) as f:
+ content = normalize(f.read())
+ assert content == expected
+
def test_feedback_to_output_true(base_app):
base_app.feedback_to_output = True
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index 302d80c8..3caf6a37 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -154,6 +154,32 @@ this is a \/multiline\/ command
assert transcript == expected
+def test_history_transcript_bad_filename(request, capsys):
+ app = CmdLineApp()
+ app.stdout = StdOut()
+ run_cmd(app, 'orate this is\na /multiline/\ncommand;\n')
+ run_cmd(app, 'speak /tmp/file.txt is not a regex')
+
+ expected = r"""(Cmd) orate this is
+> a /multiline/
+> command;
+this is a \/multiline\/ command
+(Cmd) speak /tmp/file.txt is not a regex
+\/tmp\/file.txt is not a regex
+"""
+
+ # make a tmp file
+ history_fname = '~/fakedir/this_does_not_exist.txt'
+
+ # tell the history command to create a transcript
+ run_cmd(app, 'history -t "{}"'.format(history_fname))
+
+ # read in the transcript created by the history command
+ with pytest.raises(FileNotFoundError):
+ with open(history_fname) as f:
+ transcript = f.read()
+ assert transcript == expected
+
@pytest.mark.parametrize('expected, transformed', [
# strings with zero or one slash or with escaped slashes means no regular
# expression present, so the result should just be what re.escape returns.