diff options
-rw-r--r-- | cmd2/cmd2.py | 52 | ||||
-rw-r--r-- | cmd2/parsing.py | 26 | ||||
-rw-r--r-- | tests/test_cmd2.py | 9 | ||||
-rw-r--r-- | tests/test_parsing.py | 181 |
4 files changed, 135 insertions, 133 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index c49ec0cc..1c8bb6b7 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -1720,7 +1720,7 @@ class Cmd(cmd.Cmd): :return: tuple containing (command, args, line) """ statement = self.statement_parser.parse_command_only(line) - return statement.command, statement.args, statement.command_and_args + return statement.command, statement, statement.command_and_args def onecmd_plus_hooks(self, line: str) -> bool: """Top-level function called by cmdloop() to handle parsing a line and running the command and all of its hooks. @@ -2217,8 +2217,7 @@ class Cmd(cmd.Cmd): return stop - @with_argument_list - def do_alias(self, arglist: List[str]) -> None: + def do_alias(self, statement: Statement) -> None: """Define or display aliases Usage: Usage: alias [name] | [<name> <value>] @@ -2237,22 +2236,27 @@ Usage: Usage: alias [name] | [<name> <value>] Example: alias ls !ls -lF - If you want to use redirection or pipes in the alias, then either quote the tokens with these - characters or quote the entire alias value. + If you want to use redirection or pipes in the alias, then quote them to avoid + the alias command itself from being redirected Examples: alias save_results print_results ">" out.txt - alias save_results print_results "> out.txt" - alias save_results "print_results > out.txt" + alias save_results print_results '>' out.txt """ + # Get alias arguments as a list with quotes preserved + alias_arg_list = statement.arg_list + # If no args were given, then print a list of current aliases - if not arglist: + if not alias_arg_list: for cur_alias in self.aliases: self.poutput("alias {} {}".format(cur_alias, self.aliases[cur_alias])) + return + + # Get the alias name + name = alias_arg_list[0] # The user is looking up an alias - elif len(arglist) == 1: - name = arglist[0] + if len(alias_arg_list) == 1: if name in self.aliases: self.poutput("alias {} {}".format(name, self.aliases[name])) else: @@ -2260,8 +2264,16 @@ Usage: Usage: alias [name] | [<name> <value>] # The user is creating an alias else: - name = arglist[0] - value = ' '.join(arglist[1:]) + # Unquote redirection and pipes + index = 1 + while index < len(alias_arg_list): + unquoted_arg = utils.strip_quotes(alias_arg_list[index]) + if unquoted_arg in constants.REDIRECTION_TOKENS: + alias_arg_list[index] = unquoted_arg + index += 1 + + # Build the alias value string + value = ' '.join(alias_arg_list[1:]) # Validate the alias to ensure it doesn't include weird characters # like terminators, output redirection, or whitespace @@ -2598,24 +2610,20 @@ Usage: Usage: unalias [-a] name [name ...] param = args.settable[0] self.show(args, param) - def do_shell(self, command: str) -> None: + def do_shell(self, statement: Statement) -> None: """Execute a command as if at the OS prompt. Usage: shell <command> [arguments]""" - import subprocess - try: - # Use non-POSIX parsing to keep the quotes around the tokens - tokens = shlex.split(command, posix=False) - except ValueError as err: - self.perror(err, traceback_war=False) - return + + # Get list of arguments to shell with quotes preserved + tokens = statement.arg_list # Support expanding ~ in quoted paths for index, _ in enumerate(tokens): if tokens[index]: - # Check if the token is quoted. Since shlex.split() passed, there isn't - # an unclosed quote, so we only need to check the first character. + # Check if the token is quoted. Since parsing already passed, there isn't + # an unclosed quote. So we only need to check the first character. first_char = tokens[index][0] if first_char in constants.QUOTES: tokens[index] = utils.strip_quotes(tokens[index]) diff --git a/cmd2/parsing.py b/cmd2/parsing.py index b67cef10..2a4ae56f 100644 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -5,7 +5,7 @@ import os import re import shlex -from typing import List, Tuple, Dict +from typing import List, Tuple, Dict, Union from . import constants from . import utils @@ -33,10 +33,10 @@ class Statement(str): :var multiline_command: if the command is a multiline command, the name of the command, otherwise None :type command: str or None - :var args: the arguments to the command, not including any output + :var arg_list: list of arguments to the command, not including any output redirection or terminators. quoted arguments remain quoted. - :type args: str or None + :type arg_list: list :var: argv: a list of arguments a la sys.argv. Quotes, if any, are removed from the elements of the list, and aliases and shortcuts are expanded @@ -61,7 +61,7 @@ class Statement(str): *, raw: str = None, command: str = None, - args: str = None, + arg_list: List[str] = None, argv: List[str] = None, multiline_command: str = None, terminator: str = None, @@ -78,7 +78,9 @@ class Statement(str): stmt = str.__new__(cls, obj) object.__setattr__(stmt, "raw", raw) object.__setattr__(stmt, "command", command) - object.__setattr__(stmt, "args", args) + if arg_list is None: + arg_list = [] + object.__setattr__(stmt, "arg_list", arg_list) if argv is None: argv = [] object.__setattr__(stmt, "argv", argv) @@ -96,10 +98,10 @@ class Statement(str): Quoted arguments remain quoted. """ - if self.command and self.args: - rtn = '{} {}'.format(self.command, self.args) + if self.command and self: + rtn = '{} {}'.format(self.command, self) elif self.command: - # we are trusting that if we get here that self.args is None + # there were no arguments to the command rtn = self.command else: rtn = None @@ -392,7 +394,7 @@ class StatementParser: statement = Statement('' if args is None else args, raw=line, command=command, - args=args, + arg_list=[] if len(argv) <= 1 else argv[1:], argv=list(map(lambda x: utils.strip_quotes(x), argv)), multiline_command=multiline_command, terminator=terminator, @@ -417,10 +419,9 @@ class StatementParser: values in the following attributes: - raw - command - - args Different from parse(), this method does not remove redundant whitespace - within statement.args. It does however, ensure args does not have + within the statement. It does however, ensure statement does not have leading or trailing whitespace. """ # expand shortcuts and aliases @@ -442,7 +443,7 @@ class StatementParser: # no trailing whitespace args = line[match.end(2):].rstrip() # if the command is none that means the input was either empty - # or something wierd like '>'. args should be None if we couldn't + # or something weird like '>'. args should be None if we couldn't # parse a command if not command or not args: args = None @@ -459,7 +460,6 @@ class StatementParser: statement = Statement('' if args is None else args, raw=rawinput, command=command, - args=args, multiline_command=multiline_command, ) return statement diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index b75cd102..3aeb9959 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -1759,6 +1759,15 @@ def test_alias(base_app, capsys): out = run_cmd(base_app, 'alias fake') assert out == normalize('alias fake pyscript') +def test_alias_with_quotes(base_app, capsys): + # Create the alias + out = run_cmd(base_app, 'alias fake help ">" "out file.txt"') + assert out == normalize("Alias 'fake' created") + + # Lookup the new alias (Only the redirector should be unquoted) + out = run_cmd(base_app, 'alias fake') + assert out == normalize('alias fake help > "out file.txt"') + def test_alias_lookup_invalid_alias(base_app, capsys): # Lookup invalid alias out = run_cmd(base_app, 'alias invalid') diff --git a/tests/test_parsing.py b/tests/test_parsing.py index de4c637e..e1dfa982 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -33,8 +33,7 @@ def default_parser(): def test_parse_empty_string_default(default_parser): statement = default_parser.parse('') - assert not statement.command - assert not statement.args + assert statement.command is None assert statement == '' assert statement.raw == '' @@ -54,8 +53,7 @@ def test_tokenize_default(default_parser, line, tokens): def test_parse_empty_string(parser): statement = parser.parse('') - assert not statement.command - assert not statement.args + assert statement.command is None assert statement == '' assert statement.raw == '' @@ -98,9 +96,9 @@ def test_command_and_args(parser, tokens, command, args): def test_parse_single_word(parser, line): statement = parser.parse(line) assert statement.command == line - assert statement.args is None assert statement == '' assert statement.argv == [utils.strip_quotes(line)] + assert not statement.arg_list @pytest.mark.parametrize('line,terminator', [ ('termbare;', ';'), @@ -111,9 +109,9 @@ def test_parse_single_word(parser, line): def test_parse_word_plus_terminator(parser, line, terminator): statement = parser.parse(line) assert statement.command == 'termbare' - assert statement.args is None assert statement == '' assert statement.argv == ['termbare'] + assert not statement.arg_list assert statement.terminator == terminator @pytest.mark.parametrize('line,terminator', [ @@ -125,9 +123,9 @@ def test_parse_word_plus_terminator(parser, line, terminator): def test_parse_suffix_after_terminator(parser, line, terminator): statement = parser.parse(line) assert statement.command == 'termbare' - assert statement.args is None assert statement == '' assert statement.argv == ['termbare'] + assert not statement.arg_list assert statement.terminator == terminator assert statement.suffix == 'suffx' @@ -135,72 +133,74 @@ def test_parse_command_with_args(parser): line = 'command with args' statement = parser.parse(line) assert statement.command == 'command' - assert statement.args == 'with args' - assert statement == statement.args + assert statement == 'with args' assert statement.argv == ['command', 'with', 'args'] + assert statement.arg_list == statement.argv[1:] def test_parse_command_with_quoted_args(parser): line = 'command with "quoted args" and "some not"' statement = parser.parse(line) assert statement.command == 'command' - assert statement.args == 'with "quoted args" and "some not"' - assert statement == statement.args + assert statement == 'with "quoted args" and "some not"' assert statement.argv == ['command', 'with', 'quoted args', 'and', 'some not'] + assert statement.arg_list == ['with', '"quoted args"', 'and', '"some not"'] def test_parse_command_with_args_terminator_and_suffix(parser): line = 'command with args and terminator; and suffix' statement = parser.parse(line) assert statement.command == 'command' - assert statement.args == "with args and terminator" + assert statement == "with args and terminator" assert statement.argv == ['command', 'with', 'args', 'and', 'terminator'] - assert statement == statement.args + assert statement.arg_list == statement.argv[1:] assert statement.terminator == ';' assert statement.suffix == 'and suffix' def test_parse_hashcomment(parser): statement = parser.parse('hi # this is all a comment') assert statement.command == 'hi' - assert statement.args is None assert statement == '' assert statement.argv == ['hi'] + assert not statement.arg_list def test_parse_c_comment(parser): statement = parser.parse('hi /* this is | all a comment */') assert statement.command == 'hi' - assert statement.args is None assert statement.argv == ['hi'] + assert not statement.arg_list assert statement == '' - assert not statement.pipe_to + assert statement.pipe_to is None def test_parse_c_comment_empty(parser): statement = parser.parse('/* this is | all a comment */') - assert not statement.command - assert not statement.args - assert not statement.pipe_to + assert statement.command is None + assert statement.pipe_to is None assert not statement.argv + assert not statement.arg_list assert statement == '' def test_parse_c_comment_no_closing(parser): statement = parser.parse('cat /tmp/*.txt') assert statement.command == 'cat' - assert statement.args == '/tmp/*.txt' - assert not statement.pipe_to + assert statement == '/tmp/*.txt' + assert statement.pipe_to is None assert statement.argv == ['cat', '/tmp/*.txt'] + assert statement.arg_list == statement.argv[1:] def test_parse_c_comment_multiple_opening(parser): statement = parser.parse('cat /tmp/*.txt /tmp/*.cfg') assert statement.command == 'cat' - assert statement.args == '/tmp/*.txt /tmp/*.cfg' - assert not statement.pipe_to + assert statement == '/tmp/*.txt /tmp/*.cfg' + assert statement.pipe_to is None assert statement.argv == ['cat', '/tmp/*.txt', '/tmp/*.cfg'] + assert statement.arg_list == statement.argv[1:] def test_parse_what_if_quoted_strings_seem_to_start_comments(parser): statement = parser.parse('what if "quoted strings /* seem to " start comments?') assert statement.command == 'what' - assert statement.args == 'if "quoted strings /* seem to " start comments?' + assert statement == 'if "quoted strings /* seem to " start comments?' assert statement.argv == ['what', 'if', 'quoted strings /* seem to ', 'start', 'comments?'] - assert statement == statement.args - assert not statement.pipe_to + assert statement.arg_list == ['if', '"quoted strings /* seem to "', 'start', 'comments?'] + assert statement.pipe_to is None @pytest.mark.parametrize('line',[ 'simple | piped', @@ -209,27 +209,27 @@ def test_parse_what_if_quoted_strings_seem_to_start_comments(parser): def test_parse_simple_pipe(parser, line): statement = parser.parse(line) assert statement.command == 'simple' - assert statement.args is None assert statement == '' assert statement.argv == ['simple'] + assert not statement.arg_list assert statement.pipe_to == ['piped'] def test_parse_double_pipe_is_not_a_pipe(parser): line = 'double-pipe || is not a pipe' statement = parser.parse(line) assert statement.command == 'double-pipe' - assert statement.args == '|| is not a pipe' - assert statement == statement.args + assert statement == '|| is not a pipe' assert statement.argv == ['double-pipe', '||', 'is', 'not', 'a', 'pipe'] - assert not statement.pipe_to + assert statement.arg_list == statement.argv[1:] + assert statement.pipe_to is None def test_parse_complex_pipe(parser): line = 'command with args, terminator&sufx | piped' statement = parser.parse(line) assert statement.command == 'command' - assert statement.args == "with args, terminator" + assert statement == "with args, terminator" assert statement.argv == ['command', 'with', 'args,', 'terminator'] - assert statement == statement.args + assert statement.arg_list == statement.argv[1:] assert statement.terminator == '&' assert statement.suffix == 'sufx' assert statement.pipe_to == ['piped'] @@ -243,7 +243,6 @@ def test_parse_complex_pipe(parser): def test_parse_redirect(parser,line, output): statement = parser.parse(line) assert statement.command == 'help' - assert statement.args is None assert statement == '' assert statement.output == output assert statement.output_to == 'out.txt' @@ -252,9 +251,9 @@ def test_parse_redirect_with_args(parser): line = 'output into > afile.txt' statement = parser.parse(line) assert statement.command == 'output' - assert statement.args == 'into' - assert statement == statement.args + assert statement == 'into' assert statement.argv == ['output', 'into'] + assert statement.arg_list == statement.argv[1:] assert statement.output == '>' assert statement.output_to == 'afile.txt' @@ -262,9 +261,9 @@ def test_parse_redirect_with_dash_in_path(parser): line = 'output into > python-cmd2/afile.txt' statement = parser.parse(line) assert statement.command == 'output' - assert statement.args == 'into' - assert statement == statement.args + assert statement == 'into' assert statement.argv == ['output', 'into'] + assert statement.arg_list == statement.argv[1:] assert statement.output == '>' assert statement.output_to == 'python-cmd2/afile.txt' @@ -272,9 +271,9 @@ def test_parse_redirect_append(parser): line = 'output appended to >> /tmp/afile.txt' statement = parser.parse(line) assert statement.command == 'output' - assert statement.args == 'appended to' - assert statement == statement.args + assert statement == 'appended to' assert statement.argv == ['output', 'appended', 'to'] + assert statement.arg_list == statement.argv[1:] assert statement.output == '>>' assert statement.output_to == '/tmp/afile.txt' @@ -282,22 +281,22 @@ def test_parse_pipe_and_redirect(parser): line = 'output into;sufx | pipethrume plz > afile.txt' statement = parser.parse(line) assert statement.command == 'output' - assert statement.args == 'into' - assert statement == statement.args + assert statement == 'into' assert statement.argv == ['output', 'into'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == ';' assert statement.suffix == 'sufx' assert statement.pipe_to == ['pipethrume', 'plz', '>', 'afile.txt'] - assert not statement.output - assert not statement.output_to + assert statement.output is None + assert statement.output_to is None def test_parse_output_to_paste_buffer(parser): line = 'output to paste buffer >> ' statement = parser.parse(line) assert statement.command == 'output' - assert statement.args == 'to paste buffer' - assert statement == statement.args + assert statement == 'to paste buffer' assert statement.argv == ['output', 'to', 'paste', 'buffer'] + assert statement.arg_list == statement.argv[1:] assert statement.output == '>>' def test_parse_redirect_inside_terminator(parser): @@ -307,9 +306,9 @@ def test_parse_redirect_inside_terminator(parser): line = 'has > inside;' statement = parser.parse(line) assert statement.command == 'has' - assert statement.args == '> inside' - assert statement == statement.args + assert statement == '> inside' assert statement.argv == ['has', '>', 'inside'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == ';' @pytest.mark.parametrize('line,terminator',[ @@ -325,9 +324,9 @@ def test_parse_redirect_inside_terminator(parser): def test_parse_multiple_terminators(parser, line, terminator): statement = parser.parse(line) assert statement.multiline_command == 'multiline' - assert statement.args == 'with | inside' - assert statement == statement.args + assert statement == 'with | inside' assert statement.argv == ['multiline', 'with', '|', 'inside'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == terminator def test_parse_unfinished_multiliine_command(parser): @@ -335,10 +334,10 @@ def test_parse_unfinished_multiliine_command(parser): statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'has > inside an unfinished command' - assert statement == statement.args + assert statement == 'has > inside an unfinished command' assert statement.argv == ['multiline', 'has', '>', 'inside', 'an', 'unfinished', 'command'] - assert not statement.terminator + assert statement.arg_list == statement.argv[1:] + assert statement.terminator is None @pytest.mark.parametrize('line,terminator',[ ('multiline has > inside;', ';'), @@ -350,9 +349,9 @@ def test_parse_unfinished_multiliine_command(parser): def test_parse_multiline_command_ignores_redirectors_within_it(parser, line, terminator): statement = parser.parse(line) assert statement.multiline_command == 'multiline' - assert statement.args == 'has > inside' - assert statement == statement.args + assert statement == 'has > inside' assert statement.argv == ['multiline', 'has', '>', 'inside'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == terminator def test_parse_multiline_with_incomplete_comment(parser): @@ -362,8 +361,9 @@ def test_parse_multiline_with_incomplete_comment(parser): statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'command /* with unclosed comment' + assert statement == 'command /* with unclosed comment' assert statement.argv == ['multiline', 'command', '/*', 'with', 'unclosed', 'comment'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == ';' def test_parse_multiline_with_complete_comment(parser): @@ -371,9 +371,9 @@ def test_parse_multiline_with_complete_comment(parser): statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'command is done' - assert statement == statement.args + assert statement == 'command is done' assert statement.argv == ['multiline', 'command', 'is', 'done'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == ';' def test_parse_multiline_terminated_by_empty_line(parser): @@ -381,9 +381,9 @@ def test_parse_multiline_terminated_by_empty_line(parser): statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'command ends' - assert statement == statement.args + assert statement == 'command ends' assert statement.argv == ['multiline', 'command', 'ends'] + assert statement.arg_list == statement.argv[1:] assert statement.terminator == '\n' @pytest.mark.parametrize('line,terminator',[ @@ -398,9 +398,9 @@ def test_parse_multiline_with_embedded_newline(parser, line, terminator): statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'command "with\nembedded newline"' - assert statement == statement.args + assert statement == 'command "with\nembedded newline"' assert statement.argv == ['multiline', 'command', 'with\nembedded newline'] + assert statement.arg_list == ['command', '"with\nembedded newline"'] assert statement.terminator == terminator def test_parse_multiline_ignores_terminators_in_comments(parser): @@ -408,34 +408,34 @@ def test_parse_multiline_ignores_terminators_in_comments(parser): statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'command "with term; ends" now' - assert statement == statement.args + assert statement == 'command "with term; ends" now' assert statement.argv == ['multiline', 'command', 'with term; ends', 'now'] + assert statement.arg_list == ['command', '"with term; ends"', 'now'] assert statement.terminator == '\n' def test_parse_command_with_unicode_args(parser): line = 'drink café' statement = parser.parse(line) assert statement.command == 'drink' - assert statement.args == 'café' - assert statement == statement.args + assert statement == 'café' assert statement.argv == ['drink', 'café'] + assert statement.arg_list == statement.argv[1:] def test_parse_unicode_command(parser): line = 'café au lait' statement = parser.parse(line) assert statement.command == 'café' - assert statement.args == 'au lait' - assert statement == statement.args + assert statement == 'au lait' assert statement.argv == ['café', 'au', 'lait'] + assert statement.arg_list == statement.argv[1:] def test_parse_redirect_to_unicode_filename(parser): line = 'dir home > café' statement = parser.parse(line) assert statement.command == 'dir' - assert statement.args == 'home' - assert statement == statement.args + assert statement == 'home' assert statement.argv == ['dir', 'home'] + assert statement.arg_list == statement.argv[1:] assert statement.output == '>' assert statement.output_to == 'café' @@ -452,9 +452,9 @@ def test_empty_statement_raises_exception(): app._complete_statement(' ') @pytest.mark.parametrize('line,command,args', [ - ('helpalias', 'help', None), + ('helpalias', 'help', ''), ('helpalias mycommand', 'help', 'mycommand'), - ('42', 'theanswer', None), + ('42', 'theanswer', ''), ('42 arg1 arg2', 'theanswer', 'arg1 arg2'), ('!ls', 'shell', 'ls'), ('!ls -al /tmp', 'shell', 'ls -al /tmp'), @@ -463,20 +463,15 @@ def test_empty_statement_raises_exception(): def test_parse_alias_and_shortcut_expansion(parser, line, command, args): statement = parser.parse(line) assert statement.command == command - assert statement.args == args - if statement.args is None: - assert statement == '' - else: - assert statement == statement.args + assert statement == args def test_parse_alias_on_multiline_command(parser): line = 'anothermultiline has > inside an unfinished command' statement = parser.parse(line) assert statement.multiline_command == 'multiline' assert statement.command == 'multiline' - assert statement.args == 'has > inside an unfinished command' - assert statement == statement.args - assert not statement.terminator + assert statement == 'has > inside an unfinished command' + assert statement.terminator is None @pytest.mark.parametrize('line,output', [ ('helpalias > out.txt', '>'), @@ -487,7 +482,6 @@ def test_parse_alias_on_multiline_command(parser): def test_parse_alias_redirection(parser, line, output): statement = parser.parse(line) assert statement.command == 'help' - assert statement.args is None assert statement == '' assert statement.output == output assert statement.output_to == 'out.txt' @@ -499,7 +493,6 @@ def test_parse_alias_redirection(parser, line, output): def test_parse_alias_pipe(parser, line): statement = parser.parse(line) assert statement.command == 'help' - assert statement.args is None assert statement == '' assert statement.pipe_to == ['less'] @@ -514,7 +507,6 @@ def test_parse_alias_pipe(parser, line): def test_parse_alias_terminator_no_whitespace(parser, line): statement = parser.parse(line) assert statement.command == 'help' - assert statement.args is None assert statement == '' assert statement.terminator == ';' @@ -522,8 +514,7 @@ def test_parse_command_only_command_and_args(parser): line = 'help history' statement = parser.parse_command_only(line) assert statement.command == 'help' - assert statement.args == 'history' - assert statement == statement.args + assert statement == 'history' assert statement.command_and_args == line def test_parse_command_only_emptyline(parser): @@ -533,40 +524,36 @@ def test_parse_command_only_emptyline(parser): # should be '', to retain backwards compatibility with # the cmd in the standard library assert statement.command is None - assert statement.args is None assert statement == '' assert not statement.argv - assert statement.command_and_args == None + assert not statement.arg_list + assert statement.command_and_args is None def test_parse_command_only_strips_line(parser): line = ' help history ' statement = parser.parse_command_only(line) assert statement.command == 'help' - assert statement.args == 'history' - assert statement == statement.args + assert statement == 'history' assert statement.command_and_args == line.strip() def test_parse_command_only_expands_alias(parser): line = 'fake foobar.py' statement = parser.parse_command_only(line) assert statement.command == 'pyscript' - assert statement.args == 'foobar.py' - assert statement == statement.args + assert statement == 'foobar.py' def test_parse_command_only_expands_shortcuts(parser): line = '!cat foobar.txt' statement = parser.parse_command_only(line) assert statement.command == 'shell' - assert statement.args == 'cat foobar.txt' - assert statement == statement.args + assert statement == 'cat foobar.txt' assert statement.command_and_args == 'shell cat foobar.txt' def test_parse_command_only_quoted_args(parser): line = 'l "/tmp/directory with spaces/doit.sh"' statement = parser.parse_command_only(line) assert statement.command == 'shell' - assert statement.args == 'ls -al "/tmp/directory with spaces/doit.sh"' - assert statement == statement.args + assert statement == 'ls -al "/tmp/directory with spaces/doit.sh"' assert statement.command_and_args == line.replace('l', 'shell ls -al') @pytest.mark.parametrize('line', [ @@ -598,7 +585,6 @@ def test_parse_command_only_specialchars(parser, line): def test_parse_command_only_none(parser, line): statement = parser.parse_command_only(line) assert statement.command is None - assert statement.args is None assert statement == '' def test_parse_command_only_multiline(parser): @@ -606,8 +592,7 @@ def test_parse_command_only_multiline(parser): statement = parser.parse_command_only(line) assert statement.command == 'multiline' assert statement.multiline_command == 'multiline' - assert statement.args == 'with partially "open quotes and no terminator' - assert statement == statement.args + assert statement == 'with partially "open quotes and no terminator' assert statement.command_and_args == line @@ -617,7 +602,7 @@ def test_statement_initialization(parser): assert string == statement assert statement.raw is None assert statement.command is None - assert statement.args is None + assert isinstance(statement.arg_list, list) assert isinstance(statement.argv, list) assert not statement.argv assert statement.multiline_command is None |