Fix choices validation with unannotated args

Something like:

    @cmdutils.argument('foo', choices=['one', 'two'])
    def func(foo):
        # ...

didn't actually validate the foo argument, since the inferred type of
the argument is None, and that skipped all conversion (and thus
validation).

Fixes #1871
See #1885

This is a reworked version of 12061b8bb1
which lets special parameters (count/win_id/flags) through correctly.
This commit is contained in:
Florian Bruhin 2016-08-23 22:01:21 +02:00
parent ce4dcf9e80
commit 943dc564b2
2 changed files with 37 additions and 4 deletions

View File

@ -340,12 +340,17 @@ class Command:
Args: Args:
param: The inspect.Parameter to look at. param: The inspect.Parameter to look at.
""" """
arginfo = self.get_arg_info(param)
if param.annotation is not inspect.Parameter.empty: if param.annotation is not inspect.Parameter.empty:
return param.annotation return param.annotation
elif param.default is None or param.default is inspect.Parameter.empty: elif param.default not in [None, inspect.Parameter.empty]:
return type(param.default)
elif arginfo.count or arginfo.win_id or param.kind in [
inspect.Parameter.VAR_POSITIONAL,
inspect.Parameter.VAR_KEYWORD]:
return None return None
else: else:
return type(param.default) return str
def _get_self_arg(self, win_id, param, args): def _get_self_arg(self, win_id, param, args):
"""Get the self argument for a function call. """Get the self argument for a function call.
@ -425,10 +430,10 @@ class Command:
elif typ is str: elif typ is str:
choices = self.get_arg_info(param).choices choices = self.get_arg_info(param).choices
value = argparser.type_conv(param, typ, value, str_choices=choices) value = argparser.type_conv(param, typ, value, str_choices=choices)
elif typ is None:
pass
elif typ is bool: # no type conversion for flags elif typ is bool: # no type conversion for flags
assert isinstance(value, bool) assert isinstance(value, bool)
elif typ is None:
pass
else: else:
value = argparser.type_conv(param, typ, value) value = argparser.type_conv(param, typ, value)

View File

@ -295,6 +295,34 @@ class TestRegister:
else: else:
assert cmd._get_call_args(win_id=0) == ([expected], {}) assert cmd._get_call_args(win_id=0) == ([expected], {})
def test_choices_no_annotation(self):
# https://github.com/The-Compiler/qutebrowser/issues/1871
@cmdutils.register()
@cmdutils.argument('arg', choices=['foo', 'bar'])
def fun(arg):
"""Blah."""
pass
cmd = cmdutils.cmd_dict['fun']
cmd.namespace = cmd.parser.parse_args(['fish'])
with pytest.raises(cmdexc.ArgumentTypeError):
cmd._get_call_args(win_id=0)
def test_choices_no_annotation_kwonly(self):
# https://github.com/The-Compiler/qutebrowser/issues/1871
@cmdutils.register()
@cmdutils.argument('arg', choices=['foo', 'bar'])
def fun(*, arg):
"""Blah."""
pass
cmd = cmdutils.cmd_dict['fun']
cmd.namespace = cmd.parser.parse_args(['--arg=fish'])
with pytest.raises(cmdexc.ArgumentTypeError):
cmd._get_call_args(win_id=0)
def test_pos_arg_info(self): def test_pos_arg_info(self):
@cmdutils.register() @cmdutils.register()
@cmdutils.argument('foo', choices=('a', 'b')) @cmdutils.argument('foo', choices=('a', 'b'))