Skip to content
Open
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
65 changes: 65 additions & 0 deletions tests/test_shell_completion_abort_exit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import click
import pytest
from typer.completion import shell_complete


class _FakeCompletion:
def __init__(self, cli, ctx_args, prog_name, complete_var):
self.cli = cli
self.ctx_args = ctx_args
self.prog_name = prog_name
self.complete_var = complete_var

def source(self):
# This will be overridden per-test via monkeypatch on the class
return ""

def complete(self):
# This will be overridden per-test via monkeypatch on the class
return ""


@pytest.mark.parametrize(
"instruction",
["complete_zsh", "source_zsh"],
)
@pytest.mark.parametrize(
"exc",
[click.exceptions.Abort, click.exceptions.Exit],
)
def test_shell_complete_handles_abort_and_exit(monkeypatch, capsys, instruction, exc):
monkeypatch.setattr(
click.shell_completion,
"get_completion_class",
lambda shell: _FakeCompletion,
)

if instruction.startswith("complete"):
monkeypatch.setattr(
_FakeCompletion, "complete", lambda self: (_ for _ in ()).throw(exc())
)
else:
monkeypatch.setattr(
_FakeCompletion, "source", lambda self: (_ for _ in ()).throw(exc())
)

cli = click.Command("demo")

code = shell_complete(
cli=cli,
ctx_args={},
prog_name="demo",
complete_var="_DEMO_COMPLETE",
instruction=instruction,
)

out = capsys.readouterr()
assert code == 0
assert out.out == ""
assert out.err == ""


def test_fake_completion_default_methods_are_covered():
fc = _FakeCompletion(click.Command("demo"), {}, "demo", "_DEMO_COMPLETE")
assert fc.complete() == ""
assert fc.source() == ""
14 changes: 12 additions & 2 deletions typer/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,22 @@ def shell_complete(
comp = comp_cls(cli, ctx_args, prog_name, complete_var)

if instruction == "source":
click.echo(comp.source())
try:
click.echo(comp.source())
except (click.exceptions.Abort, click.exceptions.Exit):
# During shell completion we should never show a traceback.
# If completion callbacks abort/exit, just return no output.
return 0
return 0

# Typer override to print the completion help msg with Rich
if instruction == "complete":
click.echo(comp.complete())
try:
click.echo(comp.complete())
except (click.exceptions.Abort, click.exceptions.Exit):
# During shell completion we should never show a traceback.
# If completion callbacks abort/exit, just return no output.
return 0
return 0
# Typer override end

Expand Down