summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2017-02-03 00:07:49 -0500
committerTodd Leonhardt <todd.leonhardt@gmail.com>2017-02-03 00:07:49 -0500
commita9b9cce70bd9aea81141c7e02d4be953cafe5ec2 (patch)
tree799889b05a74bd50ebe6e740caa4eea2787812e9
parent1a6dd328d834a8475acceb9211ec4f24fecc9af7 (diff)
downloadcmd2-git-a9b9cce70bd9aea81141c7e02d4be953cafe5ec2.tar.gz
Fixed optparse parsing of arguments so it works correctly both with embedded quotes and mid-line options.
-rwxr-xr-xcmd2.py25
-rw-r--r--tests/test_transcript.py8
2 files changed, 28 insertions, 5 deletions
diff --git a/cmd2.py b/cmd2.py
index 190a5c8f..4941580e 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -33,6 +33,7 @@ import optparse
import os
import platform
import re
+import shlex
import six
import subprocess
import sys
@@ -76,6 +77,13 @@ __version__ = '0.7.0'
pyparsing.ParserElement.enablePackrat()
+# If this is False (default), then non-option arguments are passed to the "do" methods using @options as one big string.
+# If this is True, then non-option arguments are passed to the "do" methods using @options as a list of strings (shlex).
+# TODO: Figure out a good way to make this configurable by the user in cmd2.Cmd __init__ or something
+# But it needs to remain False by default for reasons of backwards compatibility.
+USE_ARGUMENT_LISTS_INSTEAD_OF_STRINGS = False
+
+
class OptionParser(optparse.OptionParser):
def exit(self, status=0, msg=None):
self.values._exit = True
@@ -109,7 +117,13 @@ def remaining_args(oldArgs, newArgList):
'''
pattern = '\s+'.join(re.escape(a) for a in newArgList) + '\s*$'
matchObj = re.search(pattern, oldArgs)
- return oldArgs[matchObj.start():]
+ try:
+ remaining = oldArgs[matchObj.start():]
+ except:
+ # Don't preserve spacing, but at least we don't crash and we do preserve args and their order
+ remaining = ' '.join(newArgList)
+
+ return remaining
def _attr_get_(obj, attr):
@@ -135,7 +149,7 @@ optparse.Values.get = _attr_get_
options_defined = [] # used to distinguish --options from SQL-style --comments
-def options(option_list, arg_desc="arg"):
+def options(option_list, arg_desc="arg", arg_list=USE_ARGUMENT_LISTS_INSTEAD_OF_STRINGS):
'''Used as a decorator and passed a list of optparse-style options,
alters a cmd2 method to populate its ``opts`` argument from its
raw text argument.
@@ -165,12 +179,15 @@ def options(option_list, arg_desc="arg"):
def new_func(instance, arg):
try:
- opts, newArgList = optionParser.parse_args(arg.split())
+ opts, newArgList = optionParser.parse_args(shlex.split(arg))
# Must find the remaining args in the original argument list, but
# mustn't include the command itself
# if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command:
# newArgList = newArgList[1:]
- newArgs = remaining_args(arg, newArgList)
+ if arg_list:
+ newArgs = newArgList
+ else:
+ newArgs = remaining_args(arg, newArgList)
if isinstance(arg, ParsedString):
arg = arg.with_args_replaced(newArgs)
else:
diff --git a/tests/test_transcript.py b/tests/test_transcript.py
index 01451b95..aefd0068 100644
--- a/tests/test_transcript.py
+++ b/tests/test_transcript.py
@@ -176,10 +176,16 @@ Options:
assert _normalize(str(out)) == expected
-def test_comment_stripping(_cmdline_app):
+def test_comment_stripping(_cmdline_app, capsys):
out = run_cmd(_cmdline_app, 'speak it was /* not */ delicious! # Yuck!')
expected = _normalize("""it was delicious!""")
assert out == expected
+def test_optarser_correct_args_with_quotes_and_midline_options(_cmdline_app, capsys):
+ out = run_cmd(_cmdline_app, "speak 'This is a' -s test of the emergency broadcast system!")
+ expected = _normalize("""THIS IS A TEST OF THE EMERGENCY BROADCAST SYSTEM!""")
+ assert out == expected
+
+