fix vararg handling

This commit is contained in:
Maximilian Hils 2019-11-18 03:45:16 +01:00
parent cb723c53fa
commit f75a95acea
6 changed files with 43 additions and 31 deletions

View File

@ -41,8 +41,15 @@ def _empty_as_none(x: typing.Any) -> typing.Any:
class CommandParameter(typing.NamedTuple): class CommandParameter(typing.NamedTuple):
display_name: str name: str
type: typing.Type type: typing.Type
kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD
def __str__(self):
if self.kind is inspect.Parameter.VAR_POSITIONAL:
return f"*{self.name}"
else:
return self.name
class Command: class Command:
@ -77,16 +84,14 @@ class Command:
@property @property
def parameters(self) -> typing.List[CommandParameter]: def parameters(self) -> typing.List[CommandParameter]:
"""Returns a list of (display name, type) tuples.""" """Returns a list of CommandParameters."""
ret = [] ret = []
for name, param in self.signature.parameters.items(): for name, param in self.signature.parameters.items():
if param.kind is param.VAR_POSITIONAL: ret.append(CommandParameter(name, param.annotation, param.kind))
name = f"*{name}"
ret.append(CommandParameter(name, param.annotation))
return ret return ret
def signature_help(self) -> str: def signature_help(self) -> str:
params = " ".join(name for name, t in self.parameters) params = " ".join(str(param) for param in self.parameters)
if self.return_type: if self.return_type:
ret = f" -> {typename(self.return_type)}" ret = f" -> {typename(self.return_type)}"
else: else:
@ -184,6 +189,7 @@ class CommandManager:
CommandParameter("", mitmproxy.types.Cmd), CommandParameter("", mitmproxy.types.Cmd),
CommandParameter("", mitmproxy.types.CmdArgs), CommandParameter("", mitmproxy.types.CmdArgs),
] ]
expected: typing.Optional[CommandParameter] = None
for part in parts: for part in parts:
if part.isspace(): if part.isspace():
parsed.append( parsed.append(
@ -195,16 +201,18 @@ class CommandManager:
) )
continue continue
if next_params: if expected and expected.kind is inspect.Parameter.VAR_POSITIONAL:
expected_type: typing.Type = next_params.pop(0).type assert not next_params
elif next_params:
expected = next_params.pop(0)
else: else:
expected_type = mitmproxy.types.Unknown expected = CommandParameter("", mitmproxy.types.Unknown)
arg_is_known_command = ( arg_is_known_command = (
expected_type == mitmproxy.types.Cmd and part in self.commands expected.type == mitmproxy.types.Cmd and part in self.commands
) )
arg_is_unknown_command = ( arg_is_unknown_command = (
expected_type == mitmproxy.types.Cmd and part not in self.commands expected.type == mitmproxy.types.Cmd and part not in self.commands
) )
command_args_following = ( command_args_following = (
next_params and next_params[0].type == mitmproxy.types.CmdArgs next_params and next_params[0].type == mitmproxy.types.CmdArgs
@ -214,11 +222,11 @@ class CommandManager:
if arg_is_unknown_command and command_args_following: if arg_is_unknown_command and command_args_following:
next_params.pop(0) next_params.pop(0)
to = mitmproxy.types.CommandTypes.get(expected_type, None) to = mitmproxy.types.CommandTypes.get(expected.type, None)
valid = False valid = False
if to: if to:
try: try:
to.parse(self, expected_type, part) to.parse(self, expected.type, part)
except exceptions.TypeError: except exceptions.TypeError:
valid = False valid = False
else: else:
@ -227,7 +235,7 @@ class CommandManager:
parsed.append( parsed.append(
ParseResult( ParseResult(
value=part, value=part,
type=expected_type, type=expected.type,
valid=valid, valid=valid,
) )
) )

View File

@ -88,7 +88,7 @@ class CommandBuffer:
if parts[-1].type != mitmproxy.types.Space: if parts[-1].type != mitmproxy.types.Space:
ret.append(("text", " ")) ret.append(("text", " "))
for param in remaining: for param in remaining:
ret.append(("commander_hint", f"{param.display_name} ")) ret.append(("commander_hint", f"{param} "))
return ret return ret

View File

@ -197,7 +197,7 @@ class _ArgType(_BaseType):
return [] return []
def parse(self, manager: "CommandManager", t: type, s: str) -> str: def parse(self, manager: "CommandManager", t: type, s: str) -> str:
raise exceptions.TypeError("Arguments for unknown command.") return s
def is_valid(self, manager: "CommandManager", typ: typing.Any, val: typing.Any) -> bool: def is_valid(self, manager: "CommandManager", typ: typing.Any, val: typing.Any) -> bool:
return isinstance(val, str) return isinstance(val, str)

View File

@ -128,8 +128,7 @@ def test_arg():
with taddons.context() as tctx: with taddons.context() as tctx:
b = mitmproxy.types._ArgType() b = mitmproxy.types._ArgType()
assert b.completion(tctx.master.commands, mitmproxy.types.CmdArgs, "") == [] assert b.completion(tctx.master.commands, mitmproxy.types.CmdArgs, "") == []
with pytest.raises(mitmproxy.exceptions.TypeError): assert b.parse(tctx.master.commands, mitmproxy.types.CmdArgs, "foo") == "foo"
b.parse(tctx.master.commands, mitmproxy.types.CmdArgs, "foo")
assert b.is_valid(tctx.master.commands, mitmproxy.types.CmdArgs, 1) is False assert b.is_valid(tctx.master.commands, mitmproxy.types.CmdArgs, 1) is False

View File

@ -25,7 +25,7 @@ class TestListCompleter:
for start, options, cycle in tests: for start, options, cycle in tests:
c = commander.ListCompleter(start, options) c = commander.ListCompleter(start, options)
for expected in cycle: for expected in cycle:
assert c.cycle() == expected assert c.cycle(True) == expected
class TestCommandEdit: class TestCommandEdit:
@ -252,7 +252,7 @@ class TestCommandBuffer:
cb = commander.CommandBuffer(tctx.master) cb = commander.CommandBuffer(tctx.master)
cb.text = "foo bar" cb.text = "foo bar"
cb.cursor = len(cb.text) cb.cursor = len(cb.text)
cb.cycle_completion() cb.cycle_completion(True)
ch = commander.CommandHistory(tctx.master, 30) ch = commander.CommandHistory(tctx.master, 30)
ce = commander.CommandEdit(tctx.master, "se", ch) ce = commander.CommandEdit(tctx.master, "se", ch)
@ -261,7 +261,7 @@ class TestCommandBuffer:
ret = ce.cbuf.render() ret = ce.cbuf.render()
assert ret[0] == ('commander_command', 'set') assert ret[0] == ('commander_command', 'set')
assert ret[1] == ('text', ' ') assert ret[1] == ('text', ' ')
assert ret[2] == ('commander_hint', 'str ') assert ret[2] == ('commander_hint', '*options ')
def test_render(self): def test_render(self):
with taddons.context() as tctx: with taddons.context() as tctx:
@ -272,13 +272,13 @@ class TestCommandBuffer:
cb.text = 'set view_filter=~bq test' cb.text = 'set view_filter=~bq test'
ret = cb.render() ret = cb.render()
assert ret[0] == ('commander_command', 'set') assert ret[0] == ('commander_command', 'set')
assert ret[1] == ('commander_invalid', ' ') assert ret[1] == ('text', ' ')
assert ret[2] == ('text', 'view_filter=~bq') assert ret[2] == ('text', 'view_filter=~bq')
assert ret[3] == ('commander_invalid', ' ') assert ret[3] == ('text', ' ')
assert ret[4] == ('commander_invalid', 'test') assert ret[4] == ('text', 'test')
cb.text = "set" cb.text = "set"
ret = cb.render() ret = cb.render()
assert ret[0] == ('commander_command', 'set') assert ret[0] == ('commander_command', 'set')
assert ret[1] == ('text', ' ') assert ret[1] == ('text', ' ')
assert ret[2] == ('commander_hint', 'str ') assert ret[2] == ('commander_hint', '*options ')

View File

@ -1,10 +1,12 @@
import pytest
import mitmproxy.types
from mitmproxy import command
from mitmproxy import ctx
from mitmproxy.test.tflow import tflow from mitmproxy.test.tflow import tflow
from mitmproxy.tools.console import defaultkeys from mitmproxy.tools.console import defaultkeys
from mitmproxy.tools.console import keymap from mitmproxy.tools.console import keymap
from mitmproxy.tools.console import master from mitmproxy.tools.console import master
from mitmproxy import command
from mitmproxy import ctx
import pytest
@pytest.mark.asyncio @pytest.mark.asyncio
@ -18,10 +20,13 @@ async def test_commands_exist():
await m.load_flow(tflow()) await m.load_flow(tflow())
for binding in km.bindings: for binding in km.bindings:
results = command_manager.parse_partial(binding.command.strip()) parsed, _ = command_manager.parse_partial(binding.command.strip())
cmd = results[0][0].value cmd = parsed[0].value
args = [a.value for a in results[0][1:]] args = [
a.value for a in parsed[1:]
if a.type != mitmproxy.types.Space
]
assert cmd in m.commands.commands assert cmd in m.commands.commands