diff options
author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2017-02-03 00:07:49 -0500 |
---|---|---|
committer | Todd Leonhardt <todd.leonhardt@gmail.com> | 2017-02-03 00:07:49 -0500 |
commit | a9b9cce70bd9aea81141c7e02d4be953cafe5ec2 (patch) | |
tree | 799889b05a74bd50ebe6e740caa4eea2787812e9 | |
parent | 1a6dd328d834a8475acceb9211ec4f24fecc9af7 (diff) | |
download | cmd2-git-a9b9cce70bd9aea81141c7e02d4be953cafe5ec2.tar.gz |
Fixed optparse parsing of arguments so it works correctly both with embedded quotes and mid-line options.
-rwxr-xr-x | cmd2.py | 25 | ||||
-rw-r--r-- | tests/test_transcript.py | 8 |
2 files changed, 28 insertions, 5 deletions
@@ -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 + + |