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_genbu_params_get_overwritten() -> None: """If multiple Params have the same dest, existing Param is overwritten.""" def callback() -> None: """Does nothing.""" foo1 = Param("foo", ["-a"]) foo2 = Param("foo", ["-b"]) cli = Genbu(callback, params=[foo1, foo2]) params = cli.params assert len(params) == 1 assert params[0] is foo2
def test_cli_run_with_ambiguous_long_options() -> None: """Program should abort if long option prefix is ambiguous.""" cli = make_cli(params=[ Param("bar", ["--bar"], parser=comb.Emit(True)), Param("baz", ["--baz"], parser=comb.Emit(True)), ]) for source in ["--b", "--ba"]: with pytest.raises(SystemExit): cli.run(source.split()) cli.run(["--bar"]) cli.run(["--baz"])
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_missing_options() -> None: """Program should abort if there are missing arguments.""" cli = make_cli( params=[ Param("a", parser=comb.One(int)), Param("b", ["-b"], parser=comb.One(int)), Param("c", ["--c"], parser=comb.One(int)), ], callback=lambda a, b, c: (a, b, c), ) for source in [ "-b 1 --c 2", "1 --c 2", "1 -b 2", ]: with pytest.raises(SystemExit): cli.run(source.split()) assert cli.run("1 -b 2 --c 3".split()) == (1, 2, 3)
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_with_long_options_with_equals() -> None: """Test with long options with equals (e.g. --foo=bar).""" cli = make_cli( params=[Param("value", ["--value"], parser=comb.One(int))], callback=lambda value: value, ) cases = [("--value=5", 5), ("--valu=6", 6), ("--val=7", 7)] for source, expected in cases: assert cli.run(source.split()) == expected
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_cli_run_with_short_options() -> None: """Test with short options.""" cli = make_cli( params=[ Param( "x", ["-a", "-b", "-c"], parser=comb.One(int), aggregator=sum, ), ], callback=lambda x: x, ) assert cli.run("-a4 -b 5 -c=6".split()) == 15
def test_cli_run_with_long_options() -> None: """Test with long options.""" cli = make_cli( params=[ Param( "x", ["--foo", "--bar", "--baz"], parser=comb.One(float), aggregator=sum, ), ], callback=lambda x: x, ) assert cli.run("--foo 1 --bar 2 --baz 3".split()) == 6
def test_cli_run_with_stacked_options() -> None: """Test with stacked short options.""" def callback(a: str = "", b: str = "", c: str = "") -> str: return a + b + c cli = make_cli( params=[ Param("a", ["-a"], parser=comb.Emit("a")), Param("b", ["-b"], parser=comb.Emit("b")), Param("c", ["-c"], parser=comb.Emit("c")), ], callback=callback, ) cases = [ ("", ""), ("-a", "a"), ("-ab", "ab"), ("-ac", "ac"), ("-bc", "bc"), ("-abc", "abc"), ("-cba", "abc"), ] for source, expected in cases: assert cli.run(source.split()) == expected
def test_usage_with_multiple_examples() -> None: """usage(...) should have properly indented example lines.""" bar = Genbu( name="bar", description="Bar subcommand", callback=callback, ) foo = Genbu( name="foo", description="Foo command.", params=[ Param("help_", ["-?", "-h"], parser=comb.Emit(True)), ], callback=callback, subparsers=[bar], ) assert usage(foo).startswith("""usage: foo [options] foo <command> ...""")
def test_genbu_params_with_ellipsis( self, function: t.Callable[..., t.Any], ) -> None: """Params in params args should override inferred params.""" inferred = infer_params(function) assert Genbu(function, params=["..."]).params == inferred cli = Genbu(function, params=[ "...", Param("foo"), ]) assert cli.params[0].optargs != inferred[0].optargs for actual, expected in zip(cli.params[1:], inferred[1:]): assert actual == expected assert actual.optargs == expected.optargs assert actual.parser == expected.parser assert actual.aggregator == expected.aggregator assert actual.description == expected.description assert actual.arg_description == expected.arg_description
def test_usage_with_params() -> None: """usage(...) should contain description of options.""" cli = Genbu( name="test-cli", description="Test Genbu.", callback=callback, params=[ Param("foo", parser=comb.One(str)), Param("aaa", ["-a", "--aaa"], comb.One(str), description="Set aaa."), Param("ahh", ["--ahh"], comb.One(str), description="Set ahh."), Param("baa", ["-b", "--baa"], comb.One(str), description="Set baa."), Param("bar", ["--bar"], comb.One(str)), Param("baz", ["--baz"], comb.One(str)), ], ) assert usage(cli) == """usage: test-cli [options] <foo:str>
import sys from genbu import Genbu, Param, combinators as comb, infer_parser, usage def hello(*names: str, greeting: str = "Hello") -> str: """Say hello.""" if not names: names = ("stranger",) return "{}, {}!".format(greeting, ", ".join(names)) cli = Genbu( hello, params=[ Param("greeting", ["-g", "--greeting"], comb.One(str)), Param("names", parser=infer_parser(tuple[str, ...])), Param( "help_", ["-?", "-h", "--help"], comb.Emit(True), aggregator=lambda _: sys.exit(usage(cli)) ), ], ) if __name__ == "__main__": print(cli.run())
import sys from genbu import Genbu, Param, combinators as comb, usage def ellipsis(n: int) -> str: return n * "*" cli = Genbu(ellipsis, params=[ "...", Param( dest="_", optargs=["-?", "-h", "--help"], parser=comb.Emit(True), aggregator=lambda _: sys.exit(usage(cli)), ), ]) if __name__ == "__main__": print(cli.run())
from pathlib import Path import sys from genbu import Genbu, Param, combinators as comb, usage def cat(path: Path) -> str: """Concatenate contents of path to stdout.""" return path.read_text() cli = Genbu( cat, params=[ Param("path", ["-p", "--path"], comb.One(Path)), Param( "help_", ["-?", "-h", "--help"], comb.Emit(True), aggregator=lambda _: sys.exit(usage(cli)), ), ], ) if __name__ == "__main__": try: print(cli.run()) except Exception as exc: name = " ".join(cli.complete_name()) print(f"{name}: {exc}\nTry '{name} -h' for more information.")
name = " ".join(cli.complete_name()) or "cli" footer = f"Try '{name} -h' for more information." _usage = usage(cli, "Genbu CLI example with subcommands.", footer) if error: sys.exit(_usage) print(_usage) sys.exit(0) cli = Genbu( name=sys.argv[0], description="router example", params=[ Param( "help", ["-h", "--help"], parser=comb.Emit(True), aggregator=lambda _: show_usage(cli), ), ], subparsers=[ add.cli, cat.cli, echo.cli, ellipsis.cli, hello.cli, simple.cli, ], callback=lambda: show_usage(cli), error_handler=lambda cli, exc: show_usage(cli, error=True), ) if __name__ == "__main__": try: print(cli.run()) except Exception as exc:
"""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())
import sys from genbu import Genbu, Param, combinators as comb, usage cli = Genbu( lambda: print("Hello, world!"), params=[ Param("_", ["-?", "-h", "--help"], parser=comb.Emit(True), aggregator=lambda _: sys.exit(usage(cli))), ], ) if __name__ == "__main__": cli.run()
def test_param_with_invalid_option(dest: str, optargs: t.List[str]) -> None: """Param should raise InvalidOption if option dest has = or whitespace.""" with pytest.raises(InvalidOption): Param(dest=dest, optargs=optargs) assert Param(dest=dest)