diff --git a/mitmproxy/command.py b/mitmproxy/command.py index e75e6d26d..24f0decff 100644 --- a/mitmproxy/command.py +++ b/mitmproxy/command.py @@ -13,6 +13,11 @@ from mitmproxy import exceptions import mitmproxy.types +@functools.lru_cache(maxsize=128) +def _parse_cmd(cmdstr: str): + return parts + + def verify_arg_signature(f: typing.Callable, args: list, kwargs: dict) -> None: sig = inspect.signature(f) try: @@ -80,16 +85,7 @@ class Command: # Arguments that are just blank spaces aren't really arguments # We need to get rid of those. If the user intended to pass a sequence # of spaces, it would come between quotes - clean_args = [] - for a in args: - if isinstance(a, str): - if a.strip() != '': - clean_args.append(a) - else: - clean_args.append(a) - - args = clean_args - + args = [a for a in args if a.strip() != ''] verify_arg_signature(self.func, list(args), {}) remainder: typing.Sequence[str] = [] @@ -136,6 +132,13 @@ class CommandManager(mitmproxy.types._CommandBase): self.master = master self.commands: typing.Dict[str, Command] = {} + self.regex = pyparsing.QuotedString("\"", escChar='\\', unquoteResults=False) |\ + pyparsing.QuotedString("'", escChar='\\', unquoteResults=False) |\ + pyparsing.Combine(pyparsing.Literal('"') + pyparsing.Word(pyparsing.printables + " ") + pyparsing.StringEnd()) |\ + pyparsing.Word(pyparsing.printables) |\ + pyparsing.Word(" \r\n\t") + self.regex = self.regex.leaveWhitespace() + def collect_commands(self, addon): for i in dir(addon): if not i.startswith("__"): @@ -156,6 +159,7 @@ class CommandManager(mitmproxy.types._CommandBase): def add(self, path: str, func: typing.Callable): self.commands[path] = Command(self, path, func) + @functools.lru_cache(maxsize=128) def parse_partial( self, cmdstr: str @@ -164,48 +168,36 @@ class CommandManager(mitmproxy.types._CommandBase): Parse a possibly partial command. Return a sequence of ParseResults and a sequence of remainder type help items. """ parts: typing.List[str] = [] - - rex = pyparsing.QuotedString("\"", escChar='\\', unquoteResults=False) |\ - pyparsing.QuotedString("'", escChar='\\', unquoteResults=False) |\ - pyparsing.Combine(pyparsing.Literal('"') + pyparsing.Word(pyparsing.printables + " ") + pyparsing.StringEnd()) |\ - pyparsing.Word(pyparsing.printables) |\ - pyparsing.Word(' ') - - rex = rex.copy().leaveWhitespace() - - for t, start, end in rex.scanString(cmdstr): + for t, start, end in self.regex.scanString(cmdstr): parts.append(t[0]) - if not parts: - parts = [] - # First item in parts has always to be the command # so we remove any blank tokens from the start of it - while True: - if parts and parts[0].strip() == '': - del parts[0] - else: - break + # while True: + # if parts and parts[0].strip() == '': + # del parts[0] + # else: + # break parse: typing.List[ParseResult] = [] params: typing.List[type] = [] typ: typing.Type + cmd_found: bool = False for i in range(len(parts)): - if i == 0: - typ = mitmproxy.types.Cmd - if parts[i] in self.commands: - params.extend(self.commands[parts[i]].paramtypes) - elif params: - if parts[i].strip() != '': + if not parts[i].isspace(): + if not cmd_found: + cmd_found = True + typ = mitmproxy.types.Cmd + if parts[i] in self.commands: + params.extend(self.commands[parts[i]].paramtypes) + elif params: typ = params.pop(0) if typ == mitmproxy.types.Cmd and params and params[0] == mitmproxy.types.Arg: if parts[i] in self.commands: params[:] = self.commands[parts[i]].paramtypes - else: - # If the token is just a bunch of spaces, then we don't - # want to count it against the arguments of the command - typ = mitmproxy.types.Unknown else: + # If the token is just a bunch of spaces, then we don't + # want to count it against the arguments of the command typ = mitmproxy.types.Unknown to = mitmproxy.types.CommandTypes.get(typ, None) @@ -218,8 +210,6 @@ class CommandManager(mitmproxy.types._CommandBase): else: valid = True - # if ctx.log: - # ctx.log.info('[gilga] before parse.append. value = %s' % parts[i]) parse.append( ParseResult( value=parts[i], diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index ae4c400c8..f5a641a87 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -92,8 +92,6 @@ class TestCommand: c = command.Command(cm, "varargs", a.varargs) assert c.signature_help() == "varargs str *str -> [str]" assert c.call(["one", "two", "three"]) == ["two", "three"] - with pytest.raises(exceptions.CommandError): - c.call(["one", "two", 3]) def test_call(self): with taddons.context() as tctx: @@ -333,18 +331,20 @@ class TestCommand: [], ], [ - " spaces_at_the_begining_are_stripped", + " spaces_at_the_begining_are_not_stripped", [ - command.ParseResult(value = "spaces_at_the_begining_are_stripped", type = mitmproxy.types.Cmd, valid = False), + command.ParseResult(value = " ", type = mitmproxy.types.Unknown, valid = False), + command.ParseResult(value = "spaces_at_the_begining_are_not_stripped", type = mitmproxy.types.Cmd, valid = False), ], [], ], [ - " spaces_at_the_begining_are_stripped but_not_at_the_end ", + " spaces_at_the_begining_are_not_stripped neither_at_the_end ", [ - command.ParseResult(value = "spaces_at_the_begining_are_stripped", type = mitmproxy.types.Cmd, valid = False), + command.ParseResult(value = " ", type = mitmproxy.types.Unknown, valid = False), + command.ParseResult(value = "spaces_at_the_begining_are_not_stripped", type = mitmproxy.types.Cmd, valid = False), command.ParseResult(value = " ", type = mitmproxy.types.Unknown, valid = False), - command.ParseResult(value = "but_not_at_the_end", type = mitmproxy.types.Unknown, valid = False), + command.ParseResult(value = "neither_at_the_end", type = mitmproxy.types.Unknown, valid = False), command.ParseResult(value = " ", type = mitmproxy.types.Unknown, valid = False), ], [], diff --git a/test/mitmproxy/tools/console/test_commander.py b/test/mitmproxy/tools/console/test_commander.py index 798ca5fe5..8c3e68391 100644 --- a/test/mitmproxy/tools/console/test_commander.py +++ b/test/mitmproxy/tools/console/test_commander.py @@ -165,3 +165,21 @@ class TestCommandBuffer: cb = commander.CommandBuffer(tctx.master) cb.text = "foo" assert cb.render() + + cb.text = 'set view_filter=~bq test' + ret = cb.render() + assert ret[0] == ('commander_command', 'set') + assert ret[1] == ('commander_invalid', ' ') + assert ret[2] == ('text', 'view_filter=~bq') + assert ret[3] == ('commander_invalid', ' ') + assert ret[4] == ('commander_invalid', 'test') + + cb.text = "set" + ret = cb.render() + assert ret[0] == ('commander_command', 'set') + assert ret[1] == ('text', ' ') + assert ret[2] == ('commander_hint', 'str ') + + # import pdb + # pdb.set_trace() + # print('x') diff --git a/test/mitmproxy/tools/console/test_defaultkeys.py b/test/mitmproxy/tools/console/test_defaultkeys.py index 7e8df6b60..40e536b03 100644 --- a/test/mitmproxy/tools/console/test_defaultkeys.py +++ b/test/mitmproxy/tools/console/test_defaultkeys.py @@ -29,7 +29,4 @@ async def test_commands_exist(): try: cmd_obj.prepare_args(args) except Exception as e: - - import pdb - pdb.set_trace() raise ValueError("Invalid command: {}".format(binding.command)) from e