From d98fc4f03a88751944c0ae08ee2ac3e743f92952 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Tue, 19 Dec 2017 11:50:12 +1300 Subject: commands: partial parser now returns help for remaining arguments --- mitmproxy/command.py | 18 +++++- mitmproxy/tools/console/commander/commander.py | 2 +- mitmproxy/types.py | 2 + test/mitmproxy/test_command.py | 90 ++++++++++---------------- test/mitmproxy/test_types.py | 4 +- 5 files changed, 55 insertions(+), 61 deletions(-) diff --git a/mitmproxy/command.py b/mitmproxy/command.py index b645798d..d813568f 100644 --- a/mitmproxy/command.py +++ b/mitmproxy/command.py @@ -133,9 +133,12 @@ class CommandManager(mitmproxy.types._CommandBase): def add(self, path: str, func: typing.Callable): self.commands[path] = Command(self, path, func) - def parse_partial(self, cmdstr: str) -> typing.Sequence[ParseResult]: + def parse_partial( + self, + cmdstr: str + ) -> typing.Tuple[typing.Sequence[ParseResult], typing.Sequence[str]]: """ - Parse a possibly partial command. Return a sequence of (part, type) tuples. + Parse a possibly partial command. Return a sequence of ParseResults and a sequence of remainder type help items. """ buf = io.StringIO(cmdstr) parts = [] # type: typing.List[str] @@ -188,7 +191,16 @@ class CommandManager(mitmproxy.types._CommandBase): valid=valid, ) ) - return parse + + remhelp = [] + for x in params: + remt = mitmproxy.types.CommandTypes.get(x, None) + if not x: + remhelp = [] + break + remhelp.append(remt.display) + + return parse, remhelp def call_args(self, path: str, args: typing.Sequence[str]) -> typing.Any: """ diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index 13c80092..fd13f870 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -79,7 +79,7 @@ class CommandBuffer(): def cycle_completion(self) -> None: if not self.completion: - parts = self.master.commands.parse_partial(self.buf[:self.cursor]) + parts, remainhelp = self.master.commands.parse_partial(self.buf[:self.cursor]) last = parts[-1] ct = mitmproxy.types.CommandTypes.get(last.type, None) if ct: diff --git a/mitmproxy/types.py b/mitmproxy/types.py index 35d4ed7e..36f33943 100644 --- a/mitmproxy/types.py +++ b/mitmproxy/types.py @@ -174,6 +174,8 @@ class _CmdType(_BaseType): return list(manager.commands.keys()) def parse(self, manager: _CommandBase, t: type, s: str) -> str: + if s not in manager.commands: + raise exceptions.TypeError("Unknown command: %s" % s) return s def is_valid(self, manager: _CommandBase, typ: typing.Any, val: typing.Any) -> bool: diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py index 75b10098..25df2e61 100644 --- a/test/mitmproxy/test_command.py +++ b/test/mitmproxy/test_command.py @@ -78,39 +78,54 @@ class TestCommand: [ "foo bar", [ - command.ParseResult(value = "foo", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "foo", type = mitmproxy.types.Cmd, valid = False), command.ParseResult(value = "bar", type = str, valid = True) ], + [], ], [ - "foo 'bar", + "cmd1 'bar", [ - command.ParseResult(value = "foo", type = mitmproxy.types.Cmd, valid = True), + command.ParseResult(value = "cmd1", type = mitmproxy.types.Cmd, valid = True), command.ParseResult(value = "'bar", type = str, valid = True) - ] + ], + [], + ], + [ + "a", + [command.ParseResult(value = "a", type = mitmproxy.types.Cmd, valid = False)], + [], + ], + [ + "", + [command.ParseResult(value = "", type = mitmproxy.types.Cmd, valid = False)], + [] ], - ["a", [command.ParseResult(value = "a", type = mitmproxy.types.Cmd, valid = True)]], - ["", [command.ParseResult(value = "", type = mitmproxy.types.Cmd, valid = True)]], [ "cmd3 1", [ command.ParseResult(value = "cmd3", type = mitmproxy.types.Cmd, valid = True), command.ParseResult(value = "1", type = int, valid = True), - ] + ], + [] ], [ "cmd3 ", [ command.ParseResult(value = "cmd3", type = mitmproxy.types.Cmd, valid = True), command.ParseResult(value = "", type = int, valid = False), - ] + ], + [] ], [ "subcommand ", [ - command.ParseResult(value = "subcommand", type = mitmproxy.types.Cmd, valid = True), - command.ParseResult(value = "", type = mitmproxy.types.Cmd, valid = True), - ] + command.ParseResult( + value = "subcommand", type = mitmproxy.types.Cmd, valid = True, + ), + command.ParseResult(value = "", type = mitmproxy.types.Cmd, valid = False), + ], + ["arg"], ], [ "subcommand cmd3 ", @@ -118,13 +133,16 @@ class TestCommand: command.ParseResult(value = "subcommand", type = mitmproxy.types.Cmd, valid = True), command.ParseResult(value = "cmd3", type = mitmproxy.types.Cmd, valid = True), command.ParseResult(value = "", type = int, valid = False), - ] + ], + [] ], ] with taddons.context() as tctx: tctx.master.addons.add(TAddon()) - for s, expected in tests: - assert tctx.master.commands.parse_partial(s) == expected + for s, expected, expectedremain in tests: + current, remain = tctx.master.commands.parse_partial(s) + assert current == expected + assert expectedremain == remain def test_simple(): @@ -179,51 +197,11 @@ def test_parsearg(): with taddons.context() as tctx: tctx.master.addons.add(DummyConsole()) assert command.parsearg(tctx.master.commands, "foo", str) == "foo" - - assert command.parsearg(tctx.master.commands, "1", int) == 1 + with pytest.raises(exceptions.CommandError, match="Unsupported"): + command.parsearg(tctx.master.commands, "foo", type) with pytest.raises(exceptions.CommandError): command.parsearg(tctx.master.commands, "foo", int) - assert command.parsearg(tctx.master.commands, "true", bool) is True - assert command.parsearg(tctx.master.commands, "false", bool) is False - with pytest.raises(exceptions.CommandError): - command.parsearg(tctx.master.commands, "flobble", bool) - - assert len(command.parsearg( - tctx.master.commands, "2", typing.Sequence[flow.Flow] - )) == 2 - assert command.parsearg(tctx.master.commands, "1", flow.Flow) - with pytest.raises(exceptions.CommandError): - command.parsearg(tctx.master.commands, "2", flow.Flow) - with pytest.raises(exceptions.CommandError): - command.parsearg(tctx.master.commands, "0", flow.Flow) - with pytest.raises(exceptions.CommandError): - command.parsearg(tctx.master.commands, "foo", Exception) - - assert command.parsearg( - tctx.master.commands, "foo", typing.Sequence[str] - ) == ["foo"] - assert command.parsearg( - tctx.master.commands, "foo, bar", typing.Sequence[str] - ) == ["foo", "bar"] - - a = TAddon() - tctx.master.commands.add("choices", a.choices) - assert command.parsearg( - tctx.master.commands, "one", mitmproxy.types.Choice("choices"), - ) == "one" - with pytest.raises(exceptions.CommandError): - assert command.parsearg( - tctx.master.commands, "invalid", mitmproxy.types.Choice("choices"), - ) - - assert command.parsearg( - tctx.master.commands, "foo", mitmproxy.types.Path - ) == "foo" - assert command.parsearg( - tctx.master.commands, "foo", mitmproxy.types.Cmd - ) == "foo" - class TDec: @command.command("cmd1") diff --git a/test/mitmproxy/test_types.py b/test/mitmproxy/test_types.py index 5b1dd3a2..46e23ff3 100644 --- a/test/mitmproxy/test_types.py +++ b/test/mitmproxy/test_types.py @@ -88,7 +88,9 @@ def test_cmd(): b = mitmproxy.types._CmdType() assert b.is_valid(tctx.master.commands, mitmproxy.types.Cmd, "foo") is False assert b.is_valid(tctx.master.commands, mitmproxy.types.Cmd, "cmd1") is True - assert b.parse(tctx.master.commands, mitmproxy.types.Cmd, "foo") == "foo" + assert b.parse(tctx.master.commands, mitmproxy.types.Cmd, "cmd1") == "cmd1" + with pytest.raises(mitmproxy.exceptions.TypeError): + assert b.parse(tctx.master.commands, mitmproxy.types.Cmd, "foo") assert len( b.completion(tctx.master.commands, mitmproxy.types.Cmd, "") ) == len(tctx.master.commands.commands.keys()) -- cgit v1.2.3