def test_genbu_run_with_defaults_in_function_args(default: str) -> None: """It should use defaults when no arg is specified.""" def echo(message: str = default) -> t.Any: """Return message.""" return message cli = Genbu(echo) assert cli.run([]) == default
def test_cli_run_with_subparsers() -> None: """Test with subparsers.""" bar = Genbu( name="bar", description="Bar.", callback=lambda: "bar", ) baz = Genbu( name="baz", description="Baz.", callback=lambda: "baz", ) foo = Genbu( name="foo", description="Foo.", callback=lambda: "foo", subparsers=[bar, baz], ) assert foo.run([]) == "foo" assert foo.run(["bar"]) == "bar" assert foo.run(["baz"]) == "baz" for source in ["ba", "oof", "-h"]: with pytest.raises(SystemExit): foo.run(source.split())
def test_usage_with_really_long_list_of_commands() -> None: """usage(...) should break line.""" def make_subcli(name: str) -> Genbu: return Genbu( name=name, description="Test subcommand", callback=callback, ) cli = Genbu( name="test-cli", description="Test Genbu", subparsers=[make_subcli(3 * a) for a in string.ascii_lowercase], callback=callback, ) assert usage(cli).startswith("""usage: test-cli <command> ... Test Genbu commands: aaa, bbb, ccc, ddd, eee, fff, ggg, hhh, iii, jjj, kkk, lll, mmm, nnn, ooo, ppp, qqq, rrr, sss, ttt, uuu, vvv, www, xxx, yyy, zzz aaa""")
def test_does_not_infer_empty_params( self, function: t.Callable[..., t.Any], ) -> None: """Don't infer params if params is an empty sequence.""" cli = Genbu(function, params=[]) assert not cli.params
def test_usage_subcommand_with_no_description() -> None: """There's nothing after the command name (e.g. doesn't show "None").""" cli = Genbu( callback, subparsers=[ Genbu(callback_without_docstring, name="foo"), Genbu(callback_without_docstring, name="bar"), ], ) # There's a space after "foo", because it's left justified so its length is # divisible by 4. expected = " foo \n bar" actual = usage(cli) assert expected in actual assert "None" not in actual
def test_usage_with_header_and_footer() -> None: """usage(...) should contain header and footer.""" cli = Genbu( name="test-cli", description="Test Genbu", callback=callback, ) assert usage(cli, header="Hello.", footer="Bye.") == """usage: test-cli
def test_usage_with_no_description() -> None: """There's one blank line between usage patterns and options list.""" def callback(arg: None) -> None: # pylint: disable=W0621,W0613 ... callback(None) cli = Genbu(callback) assert usage(cli) == """usage: callback [options]
def test_usage_on_subparser() -> None: """usage(...) example line should contain subcommand name.""" bar = Genbu( name="bar", description="Bar subcommand", callback=callback, ) Genbu( name="foo", description="Foo command", callback=callback, subparsers=[bar], ) assert usage(bar).startswith("""usage: foo bar Bar subcommand""")
def show_usage(cli: Genbu, error: bool = False): 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)
def test_genbu_name_and_description_are_optional() -> None: """It should infer name and description from callback instead.""" def hello() -> None: """Hello, world!""" cli = Genbu(hello) assert cli.name == "hello" assert cli.description == "Hello, world!"
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 make_cli(**kwargs: t.Any) -> Genbu: """Genbu factory.""" def callback() -> None: """Does nothing.""" kwargs.setdefault("name", "test-cli") kwargs.setdefault("description", "Test Genbu.") kwargs.setdefault("callback", callback) return Genbu(**kwargs)
def test_usage_with_subcommands() -> None: """usage(...) should contain description of subcommands.""" bar = Genbu( name="bar", description="Bar subcommand", callback=callback, ) baz = Genbu( name="baz", description="Baz subcommand", callback=callback, ) foo = Genbu( name="foo", description="Foo command.", callback=callback, subparsers=[bar, baz], ) assert usage(foo) == """usage: foo <command> ...
def test_params_get_inferred_by_default( self, function: t.Callable[..., t.Any], ) -> None: """Infer params if None.""" cli = Genbu(function) params = cli.params assert len(params) == 3 assert params[0].dest == "foo" assert params[1].dest == "bar" assert params[2].dest == "baz"
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_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_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_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, 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()
from genbu import Genbu def echo(*args: str) -> str: """Echo strings.""" if not args: return "Usage: echo [--args <str>...]" return " ".join(args) cli = Genbu(echo) if __name__ == "__main__": print(cli.run())
def make_subcli(name: str) -> Genbu: return Genbu( name=name, description="Test subcommand", callback=callback, )
"""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, 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())
_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: print("something went wrong:", exc)
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.")
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())