Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions click_repl/_repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,13 @@ def get_command():
break

try:
# default_map passes the top-level params to the new group to
# support top-level required params that would reject the
# invocation if missing.
with group.make_context(
None, args, parent=group_ctx, default_map=old_ctx.params
) as ctx:
group.invoke(ctx)
ctx.exit()

# The group command will dispatch based on args.
old_protected_args = group_ctx.protected_args
try:
group_ctx.protected_args = args
group.invoke(group_ctx)
finally:
group_ctx.protected_args = old_protected_args
except click.ClickException as e:
e.show()
except (ClickExit, SystemExit):
Expand Down
103 changes: 101 additions & 2 deletions tests/test_repl.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import contextlib
import sys

import click
import click_repl
import pytest

from io import StringIO


@contextlib.contextmanager
def mock_stdin(text):
old_stdin = sys.stdin
try:
sys.stdin = StringIO(text)
yield sys.stdin
finally:
sys.stdin = old_stdin


def test_simple_repl():
@click.group()
Expand All @@ -21,7 +36,91 @@ def bar(foo):
click_repl.register_repl(cli)

with pytest.raises(SystemExit):
cli()
cli(args=[], prog_name="test_simple_repl")


def test_repl_dispatches_subcommand(capsys):
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
click_repl.repl(ctx)

@cli.command()
def foo():
print("Foo!")

with mock_stdin("foo\n"):
with pytest.raises(SystemExit):
cli(args=[], prog_name="test_repl_dispatch_subcommand")
assert capsys.readouterr().out == "Foo!\n"


def test_group_command_called_on_each_subcommands(capsys):
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
click_repl.repl(ctx)
else:
print("cli()")

@cli.command()
def foo():
print("Foo!")

@cli.command()
def bar():
print("Bar!")

with mock_stdin("foo\nbar\n"):
with pytest.raises(SystemExit):
cli(args=[], prog_name="test_group_command_called_on_each_subcommands")
assert capsys.readouterr().out == "cli()\nFoo!\ncli()\nBar!\n"


def test_group_argument_are_preserved(capsys):
@click.group(invoke_without_command=True)
@click.argument("argument")
@click.pass_context
def cli(ctx, argument):
if ctx.invoked_subcommand is None:
click_repl.repl(ctx)
else:
print("cli(%s)" % argument)

@cli.command()
@click.argument("argument")
def foo(argument):
print("Foo: %s!" % argument)

with mock_stdin("foo bar\n"):
with pytest.raises(SystemExit):
cli(args=["arg"], prog_name="test_group_argument_are_preserved")
assert capsys.readouterr().out == "cli(arg)\nFoo: bar!\n"


def test_chain_commands(capsys):
@click.group(invoke_without_command=True, chain=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
click_repl.repl(ctx)
else:
print("cli()")

@cli.command()
def foo():
print("Foo!")

@cli.command()
def bar():
print("Bar!")

with mock_stdin("foo bar\n"):
with pytest.raises(SystemExit):
cli(args=[], prog_name="test_chain_commands")
assert capsys.readouterr().out == "cli()\nFoo!\nBar!\n"


def test_exit_repl_function():
Expand All @@ -41,7 +140,7 @@ def repl():
click_repl.repl(click.get_current_context())

try:
cli()
cli(args=[], prog_name="test_inputs")
except (SystemExit, Exception) as e:
if (
type(e).__name__ == "prompt_toolkit.output.win32.NoConsoleScreenBufferError"
Expand Down