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_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_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_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> ...""")
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 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.")
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."
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] @pytest.mark.parametrize("source,expected,parse", [ ("", True, comb.Emit(True)), ("1 2 3", False, comb.Emit(False)), ("foo bar", "baz", comb.Emit("baz")), ("...", [], comb.Emit([])), ]) def test_emit_parse_valid( source: str, expected: t.Any, parse: comb.Parser, ) -> None: """Emit(val) parser should emit val without modifying tokens.""" tokens = as_tokens(source) before = len(tokens) assert parse(tokens).value == expected after = len(tokens) assert before == after