def test_cli_run_with_various_parameter_kinds_in_callback() -> None: """CLI arguments should properly be bound to callback params. Tested parameter kinds: - positional only - var positional - keyword only - var keyword """ def callback(a: int, *b: int, c: int, **d: int) -> t.Any: return a, b, c, d cli = make_cli( params=[ Param("a", ["-a"], parser=comb.One(int)), Param("b", ["-b"], parser=comb.Repeat(comb.One(int))), Param("c", ["-c"], parser=comb.One(int)), Param("d", ["-d"], parser=comb.Repeat( comb.And(comb.One(str), comb.One(int)), then=dict, )), ], callback=callback, ) assert cli.run("-a 1 -b 2 -c 3 -d k 4".split()) == (1, (2, ), 3, {"k": 4}) assert cli.run("-a 1 -c 2".split()) == (1, (), 2, {})
def test_usage_with_custom_arg_descriptions() -> None: """usage(...) should use custom description instead of default one.""" cli = Genbu( name="test-cli", description="Test Genbu", params=[ Param("default", ["-a"], parser=comb.Repeat(comb.One(int))), Param( "custom", ["-b"], parser=comb.Repeat(comb.One(int)), arg_description="custom-arg-description", ) ], callback=callback, ) assert usage(cli) == """usage: test-cli [options]
def test_cli_run_with_arguments() -> None: """Test with positional arguments.""" cli = make_cli( params=[Param("numbers", parser=comb.Repeat(comb.One(float)))], callback=lambda numbers: sum(numbers) # pylint: disable=W0108 ) assert cli.run([]) == 0 assert cli.run(["1.5", "2"]) == 3.5 assert cli.run(["0.45e-5"]) == 0.45e-5
def test_cli_run_without_arguments(argv: t.List[str]) -> None: """It should use sys.argv[1:].""" with pytest.MonkeyPatch.context() as mp: mp.setattr(sys, "argv", argv) cli = make_cli( params=[ Param("arg", parser=comb.Repeat(comb.One(str), then=list)), ], callback=lambda arg: arg, ) assert cli.run() == argv[1:]
def test_repeat_parse_infinite() -> None: """Repeat(p) parser should break out of infinite loop. Ex: Emit always parses successfully, so Repeat(Emit(...)) would keep parsing forever if it doesn't break out of the loop. """ parse = comb.Repeat(comb.Emit(True)) tokens = as_tokens("1 2 3 4 5") before = tuple(tokens) result = parse(tokens) after = tuple(tokens) assert before == after == ("1", "2", "3", "4", "5") assert result.value == [True]
def test_parser_pretty() -> None: """Parser.pretty should use template.""" assert comb.Repeat(comb.One(int)).pretty() == "<[int...]>" parser = comb.And(comb.Or(comb.One(float), comb.Emit(False))) assert parser.pretty("test: {}, bye.") == "test: [float], bye."
"""And(f, g, ...) parser should parse space separated list.""" assert parse(as_tokens(source)).value == expected @pytest.mark.parametrize("source,parse", [ ("1.5", comb.And(comb.One(int))), ("hello 5.6", comb.And(comb.One(str), comb.One(int))), ]) def test_and_parse_invalid(source: str, parse: comb.Parser) -> None: """And(f, g, ...) parser should raise CantParse.""" with pytest.raises(comb.CantParse): parse(as_tokens(source)) @pytest.mark.parametrize("source,expected,parse", [ ("", [], comb.Repeat(comb.One(float), then=list)), ("ok", (), comb.Repeat(comb.One(float), then=tuple)), ("5 5 5 5 stop", 20, comb.Repeat(comb.One(int), then=sum)), ]) def test_repeat_parse_valid( source: str, expected: t.Any, parse: comb.Parser, ) -> None: """Repeat(p) parser should run p parser as many times as needed.""" assert parse(as_tokens(source)).value == expected @pytest.mark.parametrize("source,parse", [ ("a", comb.Repeat(comb.One(int))), ("1.5", comb.Repeat(comb.One(int))),
"""Add items.""" import sys from genbu import Genbu, Param, combinators as comb, usage def add(*args: float) -> float: """Add items.""" return sum(args) cli = Genbu( add, params=[ Param("args", parser=comb.Repeat(comb.One(float))), Param( "_", ["-?", "-h", "-H"], description="Show help message", parser=comb.Emit(True), aggregator=lambda _: sys.exit(usage(cli)), ), ], ) if __name__ == "__main__": print(cli.run())