1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
# stdlib
import re
from typing import Callable, ContextManager, Type
# 3rd party
import click
import pytest
from coincidence.regressions import AdvancedFileRegressionFixture
from pytest_regressions.file_regression import FileRegressionFixture
# this package
from consolekit import click_command
from consolekit.testing import CliRunner, Result, _click_major
from consolekit.tracebacks import TracebackHandler, handle_tracebacks, traceback_handler, traceback_option
exceptions = pytest.mark.parametrize(
"exception",
[
pytest.param(FileNotFoundError("foo.txt"), id="FileNotFoundError"),
pytest.param(FileExistsError("foo.txt"), id="FileExistsError"),
pytest.param(Exception("Something's awry!"), id="Exception"),
pytest.param(ValueError("'age' must be >= 0"), id="ValueError"),
pytest.param(TypeError("Expected type int, got type str"), id="TypeError"),
pytest.param(NameError("name 'hello' is not defined"), id="NameError"),
pytest.param(SyntaxError("invalid syntax"), id="SyntaxError"),
]
)
contextmanagers = pytest.mark.parametrize(
"contextmanager, exit_code",
[
pytest.param(handle_tracebacks, 1, id="handle_tracebacks"),
pytest.param(traceback_handler, 1, id="traceback_handler"),
pytest.param(TracebackHandler(), 1, id="TracebackHandler"),
pytest.param(TracebackHandler(SystemExit(1)), 1, id="TracebackHandler_SystemExit"),
pytest.param(TracebackHandler(SystemExit(2)), 2, id="TracebackHandler_SystemExit_2"),
]
)
@exceptions
@contextmanagers
def test_handle_tracebacks(
exception: BaseException,
contextmanager: Callable[..., ContextManager],
exit_code: int,
advanced_file_regression: AdvancedFileRegressionFixture,
cli_runner: CliRunner,
):
@click.command()
def demo() -> None:
with contextmanager():
raise exception
result: Result = cli_runner.invoke(demo, catch_exceptions=False)
result.check_stdout(advanced_file_regression)
assert result.exit_code == exit_code
@exceptions
def test_handle_tracebacks_show_traceback(exception: Exception, cli_runner: CliRunner):
@click.command()
def demo() -> None:
with handle_tracebacks(show_traceback=True):
raise exception
with pytest.raises(type(exception), match=re.escape(str(exception))):
cli_runner.invoke(demo, catch_exceptions=False)
@pytest.mark.parametrize("exception", [EOFError(), KeyboardInterrupt(), click.Abort()])
@contextmanagers
def test_handle_tracebacks_ignored_exceptions_click(
exception: Exception,
contextmanager: Callable[..., ContextManager],
cli_runner: CliRunner,
exit_code: int,
):
@click.command()
def demo() -> None:
with contextmanager():
raise exception
result: Result = cli_runner.invoke(demo, catch_exceptions=False)
assert result.stdout.strip() == "Aborted!"
assert result.exit_code == 1
@pytest.mark.parametrize("exception", [EOFError, KeyboardInterrupt, click.Abort, SystemExit])
@contextmanagers
def test_handle_tracebacks_ignored_exceptions(
exception: Type[Exception],
contextmanager: Callable[..., ContextManager],
exit_code: int,
):
with pytest.raises(exception): # noqa: PT012 # skipcq
with contextmanager():
raise exception
@pytest.mark.parametrize(
"exception, code",
[
pytest.param(click.UsageError("Message"), 2, id="click.UsageError"),
pytest.param(click.BadParameter("Message"), 2, id="click.BadParameter"),
pytest.param(
click.FileError("Message"),
1,
id="click.FileError",
marks=pytest.mark.skipif(_click_major == 8, reason="Output differs on Click 8")
),
pytest.param(
click.FileError("Message"),
1,
id="click.FileError_8",
marks=pytest.mark.skipif(_click_major != 8, reason="Output differs on Click 8")
),
pytest.param(click.ClickException("Message"), 1, id="click.ClickException"),
]
)
@contextmanagers
@pytest.mark.parametrize(
"click_version",
[
pytest.param(
'7',
marks=pytest.mark.skipif(_click_major == 8, reason="Output differs on click 8"),
),
pytest.param(
'8',
marks=pytest.mark.skipif(_click_major != 8, reason="Output differs on click 8"),
),
]
)
def test_handle_tracebacks_ignored_click(
exception: Exception,
contextmanager: Callable[..., ContextManager],
advanced_file_regression: AdvancedFileRegressionFixture,
code: int,
cli_runner: CliRunner,
click_version: str,
exit_code: int,
):
@click.command()
def demo() -> None:
with contextmanager():
raise exception
result = cli_runner.invoke(demo, catch_exceptions=False)
result.check_stdout(advanced_file_regression)
# if code == 1 and exit_code == 2:
# code = 2
assert result.exit_code == code
def test_traceback_option(file_regression: FileRegressionFixture, cli_runner: CliRunner):
@traceback_option()
@click_command()
def main(show_traceback: bool) -> None:
print(show_traceback)
result = cli_runner.invoke(main, catch_exceptions=False)
assert result.stdout.rstrip() == "False"
assert result.exit_code == 0
result = cli_runner.invoke(main, args="--traceback")
assert result.stdout.rstrip() == "True"
assert result.exit_code == 0
result = cli_runner.invoke(main, args="-T")
assert result.stdout.rstrip() == "True"
assert result.exit_code == 0
result = cli_runner.invoke(main, args="-h")
result.check_stdout(file_regression)
assert result.exit_code == 0
def test_traceback_handler_abort(capsys):
TH = TracebackHandler(ValueError("foo"))
with pytest.raises(ValueError, match="foo$"):
TH.abort("Hello World")
assert capsys.readouterr().err == "Hello World\n"
with pytest.raises(ValueError, match="foo$"):
TH.abort(["Hello", "Everybody"])
assert capsys.readouterr().err == "HelloEverybody\n"
|