Skip to content

Commit f4c3013

Browse files
committed
Added a bunch of unit tests
Also improved error handling in some exceptional cases.
1 parent 02f234f commit f4c3013

3 files changed

Lines changed: 204 additions & 37 deletions

File tree

cmd2.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ def exit(self, status=0, msg=None):
150150
151151
We override exit so it doesn't automatically exit the application.
152152
"""
153-
self.values._exit = True
153+
if self.values is not None:
154+
self.values._exit = True
155+
154156
if msg:
155157
print(msg)
156158

@@ -159,10 +161,9 @@ def print_help(self, *args, **kwargs):
159161
160162
We override it so that before the standard optparse help, it prints the do_* method docstring, if available.
161163
"""
162-
try:
164+
if self._func.__doc__:
163165
print(self._func.__doc__)
164-
except AttributeError:
165-
pass
166+
166167
optparse.OptionParser.print_help(self, *args, **kwargs)
167168

168169
def error(self, msg):
@@ -195,9 +196,13 @@ def remaining_args(opts_plus_args, arg_list):
195196

196197
def _which(editor):
197198
try:
198-
return subprocess.Popen(['which', editor], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0]
199-
except OSError:
200-
return None
199+
if six.PY3:
200+
editor_path = subprocess.check_output(['which', editor], stderr=subprocess.STDOUT, encoding='utf-8').strip()
201+
else:
202+
editor_path = subprocess.check_output(['which', editor], stderr=subprocess.STDOUT).strip()
203+
except subprocess.CalledProcessError:
204+
editor_path = None
205+
return editor_path
201206

202207

203208
def strip_quotes(arg):
@@ -297,7 +302,7 @@ def new_func(instance, arg):
297302
result = func(instance, arg, opts)
298303
return result
299304

300-
new_func.__doc__ = '%s\n%s' % (func.__doc__, option_parser.format_help())
305+
new_func.__doc__ = '%s%s' % (func.__doc__ + '\n' if func.__doc__ else '', option_parser.format_help())
301306
return new_func
302307

303308
return option_setup
@@ -746,7 +751,7 @@ def _redirect_output(self, statement):
746751

747752
# NOTE: We couldn't get a real pipe working via subprocess for Python 3.x prior to 3.5.
748753
# So to allow compatibility with Python 2.7 and 3.3+ we are redirecting output to a temporary file.
749-
# And once command is complete we are using the "cat" shell command to pipe to whatever.
754+
# And once command is complete we are the temp file as stdin for the shell command to pipe to.
750755
# TODO: Once support for Python 3.x prior to 3.5 is no longer necessary, replace with a real subprocess pipe
751756

752757
# Redirect stdout to a temporary file
@@ -789,8 +794,8 @@ def _restore_output(self, statement):
789794
# Pipe the contents of tempfile to the specified shell command
790795
with open(self._temp_filename) as fd:
791796
pipe_proc = subprocess.Popen(shlex.split(statement.parsed.pipeTo), stdin=fd,
792-
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
793-
output, err = pipe_proc.communicate()
797+
stdout=subprocess.PIPE)
798+
output, _ = pipe_proc.communicate()
794799

795800
if six.PY3:
796801
self.stdout.write(output.decode())
@@ -970,12 +975,8 @@ def do_help(self, arg):
970975
funcname = self._func_named(arg)
971976
if funcname:
972977
fn = getattr(self, funcname)
973-
try:
974-
# Use Optparse help for @options commands
975-
fn.optionParser.print_help(file=self.stdout)
976-
except AttributeError:
977-
# No special behavior needed, delegate to cmd base class do_help()
978-
cmd.Cmd.do_help(self, funcname[3:])
978+
# No special behavior needed, delegate to cmd base class do_help()
979+
cmd.Cmd.do_help(self, funcname[3:])
979980
else:
980981
# Show a menu of what commands help can be gotten for
981982
self._help_menu()
@@ -1140,7 +1141,7 @@ def do_shell(self, command):
11401141
"""Execute a command as if at the OS prompt.
11411142
11421143
Usage: shell <command> [arguments]"""
1143-
proc = subprocess.Popen(command, stdout=self.stdout, stderr=sys.stderr, shell=True)
1144+
proc = subprocess.Popen(command, stdout=self.stdout, shell=True)
11441145
proc.communicate()
11451146

11461147
def path_complete(self, text, line, begidx, endidx, dir_exe_only=False, dir_only=False):
@@ -1573,7 +1574,7 @@ def do_save(self, arg):
15731574
try:
15741575
args = self.saveparser.parseString(arg)
15751576
except pyparsing.ParseException:
1576-
self.perror('Could not understand save target %s' % arg)
1577+
self.perror('Could not understand save target %s' % arg, traceback_war=False)
15771578
raise SyntaxError(self.do_save.__doc__)
15781579

15791580
# If a filename was supplied then use that, otherwise use a temp file
@@ -1588,21 +1589,20 @@ def do_save(self, arg):
15881589
elif args.idx:
15891590
saveme = self.history[int(args.idx) - 1]
15901591
else:
1591-
saveme = ''
15921592
# Wrap in try to deal with case of empty history
15931593
try:
15941594
# Since this save command has already been added to history, need to go one more back for previous
15951595
saveme = self.history[-2]
15961596
except IndexError:
1597-
pass
1597+
self.perror('History is empty, nothing to save.', traceback_war=False)
1598+
return
15981599
try:
15991600
f = open(os.path.expanduser(fname), 'w')
16001601
f.write(saveme)
16011602
f.close()
16021603
self.pfeedback('Saved to {}'.format(fname))
1603-
except Exception:
1604-
self.perror('Error saving {}'.format(fname))
1605-
raise
1604+
except Exception as e:
1605+
self.perror('Saving {!r} - {}'.format(fname, e.strerror), traceback_war=False)
16061606

16071607
def do__relative_load(self, file_path):
16081608
"""Runs commands in script file that is encoded as either ASCII or UTF-8 text.

0 commit comments

Comments
 (0)