python-click/tests/test_commands.py
2021-10-09 21:31:57 -04:00

353 lines
9.3 KiB
Python

import re
import click
def test_other_command_invoke(runner):
@click.command()
@click.pass_context
def cli(ctx):
return ctx.invoke(other_cmd, arg=42)
@click.command()
@click.argument("arg", type=click.INT)
def other_cmd(arg):
click.echo(arg)
result = runner.invoke(cli, [])
assert not result.exception
assert result.output == "42\n"
def test_other_command_forward(runner):
cli = click.Group()
@cli.command()
@click.option("--count", default=1)
def test(count):
click.echo(f"Count: {count:d}")
@cli.command()
@click.option("--count", default=1)
@click.pass_context
def dist(ctx, count):
ctx.forward(test)
ctx.invoke(test, count=42)
result = runner.invoke(cli, ["dist"])
assert not result.exception
assert result.output == "Count: 1\nCount: 42\n"
def test_forwarded_params_consistency(runner):
cli = click.Group()
@cli.command()
@click.option("-a")
@click.pass_context
def first(ctx, **kwargs):
click.echo(f"{ctx.params}")
@cli.command()
@click.option("-a")
@click.option("-b")
@click.pass_context
def second(ctx, **kwargs):
click.echo(f"{ctx.params}")
ctx.forward(first)
result = runner.invoke(cli, ["second", "-a", "foo", "-b", "bar"])
assert not result.exception
assert result.output == "{'a': 'foo', 'b': 'bar'}\n{'a': 'foo', 'b': 'bar'}\n"
def test_auto_shorthelp(runner):
@click.group()
def cli():
pass
@cli.command()
def short():
"""This is a short text."""
@cli.command()
def special_chars():
"""Login and store the token in ~/.netrc."""
@cli.command()
def long():
"""This is a long text that is too long to show as short help
and will be truncated instead."""
result = runner.invoke(cli, ["--help"])
assert (
re.search(
r"Commands:\n\s+"
r"long\s+This is a long text that is too long to show as short help"
r"\.\.\.\n\s+"
r"short\s+This is a short text\.\n\s+"
r"special-chars\s+Login and store the token in ~/.netrc\.\s*",
result.output,
)
is not None
)
def test_no_args_is_help(runner):
@click.command(no_args_is_help=True)
def cli():
pass
result = runner.invoke(cli, [])
assert result.exit_code == 0
assert "Show this message and exit." in result.output
def test_default_maps(runner):
@click.group()
def cli():
pass
@cli.command()
@click.option("--name", default="normal")
def foo(name):
click.echo(name)
result = runner.invoke(cli, ["foo"], default_map={"foo": {"name": "changed"}})
assert not result.exception
assert result.output == "changed\n"
def test_group_with_args(runner):
@click.group()
@click.argument("obj")
def cli(obj):
click.echo(f"obj={obj}")
@cli.command()
def move():
click.echo("move")
result = runner.invoke(cli, [])
assert result.exit_code == 0
assert "Show this message and exit." in result.output
result = runner.invoke(cli, ["obj1"])
assert result.exit_code == 2
assert "Error: Missing command." in result.output
result = runner.invoke(cli, ["obj1", "--help"])
assert result.exit_code == 0
assert "Show this message and exit." in result.output
result = runner.invoke(cli, ["obj1", "move"])
assert result.exit_code == 0
assert result.output == "obj=obj1\nmove\n"
def test_base_command(runner):
import optparse
@click.group()
def cli():
pass
class OptParseCommand(click.BaseCommand):
def __init__(self, name, parser, callback):
super().__init__(name)
self.parser = parser
self.callback = callback
def parse_args(self, ctx, args):
try:
opts, args = parser.parse_args(args)
except Exception as e:
ctx.fail(str(e))
ctx.args = args
ctx.params = vars(opts)
def get_usage(self, ctx):
return self.parser.get_usage()
def get_help(self, ctx):
return self.parser.format_help()
def invoke(self, ctx):
ctx.invoke(self.callback, ctx.args, **ctx.params)
parser = optparse.OptionParser(usage="Usage: foo test [OPTIONS]")
parser.add_option(
"-f", "--file", dest="filename", help="write report to FILE", metavar="FILE"
)
parser.add_option(
"-q",
"--quiet",
action="store_false",
dest="verbose",
default=True,
help="don't print status messages to stdout",
)
def test_callback(args, filename, verbose):
click.echo(" ".join(args))
click.echo(filename)
click.echo(verbose)
cli.add_command(OptParseCommand("test", parser, test_callback))
result = runner.invoke(
cli, ["test", "-f", "test.txt", "-q", "whatever.txt", "whateverelse.txt"]
)
assert not result.exception
assert result.output.splitlines() == [
"whatever.txt whateverelse.txt",
"test.txt",
"False",
]
result = runner.invoke(cli, ["test", "--help"])
assert not result.exception
assert result.output.splitlines() == [
"Usage: foo test [OPTIONS]",
"",
"Options:",
" -h, --help show this help message and exit",
" -f FILE, --file=FILE write report to FILE",
" -q, --quiet don't print status messages to stdout",
]
def test_object_propagation(runner):
for chain in False, True:
@click.group(chain=chain)
@click.option("--debug/--no-debug", default=False)
@click.pass_context
def cli(ctx, debug):
if ctx.obj is None:
ctx.obj = {}
ctx.obj["DEBUG"] = debug
@cli.command()
@click.pass_context
def sync(ctx):
click.echo(f"Debug is {'on' if ctx.obj['DEBUG'] else 'off'}")
result = runner.invoke(cli, ["sync"])
assert result.exception is None
assert result.output == "Debug is off\n"
def test_other_command_invoke_with_defaults(runner):
@click.command()
@click.pass_context
def cli(ctx):
return ctx.invoke(other_cmd)
@click.command()
@click.option("--foo", type=click.INT, default=42)
@click.pass_context
def other_cmd(ctx, foo):
assert ctx.info_name == "other-cmd"
click.echo(foo)
result = runner.invoke(cli, [])
assert not result.exception
assert result.output == "42\n"
def test_invoked_subcommand(runner):
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
click.echo("no subcommand, use default")
ctx.invoke(sync)
else:
click.echo("invoke subcommand")
@cli.command()
def sync():
click.echo("in subcommand")
result = runner.invoke(cli, ["sync"])
assert not result.exception
assert result.output == "invoke subcommand\nin subcommand\n"
result = runner.invoke(cli)
assert not result.exception
assert result.output == "no subcommand, use default\nin subcommand\n"
def test_aliased_command_canonical_name(runner):
class AliasedGroup(click.Group):
def get_command(self, ctx, cmd_name):
return push
def resolve_command(self, ctx, args):
_, command, args = super().resolve_command(ctx, args)
return command.name, command, args
cli = AliasedGroup()
@cli.command()
def push():
click.echo("push command")
result = runner.invoke(cli, ["pu", "--help"])
assert not result.exception
assert result.output.startswith("Usage: root push [OPTIONS]")
def test_group_add_command_name(runner):
cli = click.Group("cli")
cmd = click.Command("a", params=[click.Option(["-x"], required=True)])
cli.add_command(cmd, "b")
# Check that the command is accessed through the registered name,
# not the original name.
result = runner.invoke(cli, ["b"], default_map={"b": {"x": 3}})
assert result.exit_code == 0
def test_unprocessed_options(runner):
@click.command(context_settings=dict(ignore_unknown_options=True))
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
@click.option("--verbose", "-v", count=True)
def cli(verbose, args):
click.echo(f"Verbosity: {verbose}")
click.echo(f"Args: {'|'.join(args)}")
result = runner.invoke(cli, ["-foo", "-vvvvx", "--muhaha", "x", "y", "-x"])
assert not result.exception
assert result.output.splitlines() == [
"Verbosity: 4",
"Args: -foo|-x|--muhaha|x|y|-x",
]
def test_deprecated_in_help_messages(runner):
@click.command(deprecated=True)
def cmd_with_help():
"""CLI HELP"""
pass
result = runner.invoke(cmd_with_help, ["--help"])
assert "(Deprecated)" in result.output
@click.command(deprecated=True)
def cmd_without_help():
pass
result = runner.invoke(cmd_without_help, ["--help"])
assert "(Deprecated)" in result.output
def test_deprecated_in_invocation(runner):
@click.command(deprecated=True)
def deprecated_cmd():
pass
result = runner.invoke(deprecated_cmd)
assert "DeprecationWarning:" in result.output