diff options
| author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2018-06-07 00:26:35 -0400 |
|---|---|---|
| committer | Todd Leonhardt <todd.leonhardt@gmail.com> | 2018-06-07 00:26:35 -0400 |
| commit | 91a69cb3a6c4882aa6c2f953582444e5918aca07 (patch) | |
| tree | c2354f1cbf98be74c8e86aaa062ef443cec4ea76 /cmd2.py | |
| parent | 05e15985e4a593c8b14151c705315e8985cff00e (diff) | |
| download | cmd2-git-91a69cb3a6c4882aa6c2f953582444e5918aca07.tar.gz | |
Backported file open crash fixes for Issue #430 to python2 branch
Also:
- Bumped version on python2 branch to 0.8.8
- Updated CHANGELOG
Diffstat (limited to 'cmd2.py')
| -rwxr-xr-x | cmd2.py | 106 |
1 files changed, 75 insertions, 31 deletions
@@ -229,7 +229,7 @@ if six.PY2 and sys.platform.startswith('lin'): pass -__version__ = '0.8.7' +__version__ = '0.8.8' # Pyparsing enablePackrat() can greatly speed up parsing, but problems have been seen in Python 3 in the past pyparsing.ParserElement.enablePackrat() @@ -2470,7 +2470,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 @@ -2586,7 +2586,11 @@ class Cmd(cmd.Cmd): mode = 'w' if statement.parsed.output == 2 * self.redirector: mode = 'a' - sys.stdout = self.stdout = open(os.path.expanduser(statement.parsed.outputTo), mode) + try: + sys.stdout = self.stdout = open(os.path.expanduser(statement.parsed.outputTo), mode) + except (FILE_NOT_FOUND_ERROR, IOError) as ex: + self.perror('Not Redirecting because - {}'.format(ex), traceback_war=False) + self.redirecting = False else: sys.stdout = self.stdout = tempfile.TemporaryFile(mode="w+") if statement.parsed.output == '>>': @@ -3638,34 +3642,7 @@ a..b, a:b, a:, ..b items by indices (inclusive) except Exception as e: self.perror('Saving {!r} - {}'.format(args.output_file, e), traceback_war=False) elif args.transcript: - # Make sure echo is on so commands print to standard out - saved_echo = self.echo - self.echo = True - - # Redirect stdout to the transcript file - saved_self_stdout = self.stdout - self.stdout = open(args.transcript, 'w') - - # Run all of the commands in the history with output redirected to transcript and echo on - self.runcmds_plus_hooks(history) - - # Restore stdout to its original state - self.stdout.close() - self.stdout = saved_self_stdout - - # Set echo back to its original state - self.echo = saved_echo - - # Post-process the file to escape un-escaped "/" regex escapes - with open(args.transcript, 'r') as fin: - data = fin.read() - post_processed_data = data.replace('/', '\/') - with open(args.transcript, 'w') as fout: - fout.write(post_processed_data) - - plural = 's' if len(history) > 1 else '' - self.pfeedback('{} command{} and outputs saved to transcript file {!r}'.format(len(history), plural, - args.transcript)) + self._generate_transcript(history, args.transcript) else: # Display the history items retrieved for hi in history: @@ -3674,6 +3651,73 @@ a..b, a:b, a:, ..b items by indices (inclusive) else: self.poutput(hi.pr()) + def _generate_transcript(self, history, transcript_file): + """Generate a transcript file from a given history of commands.""" + # Save the current echo state, and turn it off. We inject commands into the + # output using a different mechanism + import io + + saved_echo = self.echo + self.echo = False + + # Redirect stdout to the transcript file + saved_self_stdout = self.stdout + + # The problem with supporting regular expressions in transcripts + # is that they shouldn't be processed in the command, just the output. + # In addition, when we generate a transcript, any slashes in the output + # are not really intended to indicate regular expressions, so they should + # be escaped. + # + # We have to jump through some hoops here in order to catch the commands + # separately from the output and escape the slashes in the output. + transcript = '' + for history_item in history: + # build the command, complete with prompts. When we replay + # the transcript, we look for the prompts to separate + # the command from the output + first = True + command = '' + for line in history_item.splitlines(): + if first: + command += '{}{}\n'.format(self.prompt, line) + first = False + else: + command += '{}{}\n'.format(self.continuation_prompt, line) + transcript += command + # create a new string buffer and set it to stdout to catch the output + # of the command + membuf = io.StringIO() + self.stdout = membuf + # then run the command and let the output go into our buffer + self.onecmd_plus_hooks(history_item) + # rewind the buffer to the beginning + membuf.seek(0) + # get the output out of the buffer + output = membuf.read() + # and add the regex-escaped output to the transcript + transcript += output.replace('/', '\/') + + # Restore stdout to its original state + self.stdout = saved_self_stdout + # Set echo back to its original state + self.echo = saved_echo + + # finally, we can write the transcript out to the file + try: + with open(transcript_file, 'w') as fout: + fout.write(transcript) + except (FILE_NOT_FOUND_ERROR, IOError) as ex: + self.perror('Failed to save transcript: {}'.format(ex), traceback_war=False) + else: + # 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): """Edit a file in a text editor. |
