mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-01-08 22:07:06 +00:00
Raise CLI errors in debug mode (#771)
This commit is contained in:
parent
29e6b92b1e
commit
5ba3676422
44
kasa/cli.py
44
kasa/cli.py
@ -90,19 +90,43 @@ click.anyio_backend = "asyncio"
|
|||||||
pass_dev = click.make_pass_decorator(Device)
|
pass_dev = click.make_pass_decorator(Device)
|
||||||
|
|
||||||
|
|
||||||
class ExceptionHandlerGroup(click.Group):
|
def CatchAllExceptions(cls):
|
||||||
"""Group to capture all exceptions and print them nicely.
|
"""Capture all exceptions and prints them nicely.
|
||||||
|
|
||||||
Idea from https://stackoverflow.com/a/44347763
|
Idea from https://stackoverflow.com/a/44347763 and
|
||||||
|
https://stackoverflow.com/questions/52213375
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def _handle_exception(debug, exc):
|
||||||
"""Run the coroutine in the event loop and print any exceptions."""
|
if isinstance(exc, click.ClickException):
|
||||||
try:
|
|
||||||
asyncio.get_event_loop().run_until_complete(self.main(*args, **kwargs))
|
|
||||||
except Exception as ex:
|
|
||||||
echo(f"Got error: {ex!r}")
|
|
||||||
raise
|
raise
|
||||||
|
echo(f"Raised error: {exc}")
|
||||||
|
if debug:
|
||||||
|
raise
|
||||||
|
echo("Run with --debug enabled to see stacktrace")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
class _CommandCls(cls):
|
||||||
|
_debug = False
|
||||||
|
|
||||||
|
async def make_context(self, info_name, args, parent=None, **extra):
|
||||||
|
self._debug = any(
|
||||||
|
[arg for arg in args if arg in ["--debug", "-d", "--verbose", "-v"]]
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
return await super().make_context(
|
||||||
|
info_name, args, parent=parent, **extra
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
_handle_exception(self._debug, exc)
|
||||||
|
|
||||||
|
async def invoke(self, ctx):
|
||||||
|
try:
|
||||||
|
return await super().invoke(ctx)
|
||||||
|
except Exception as exc:
|
||||||
|
_handle_exception(self._debug, exc)
|
||||||
|
|
||||||
|
return _CommandCls
|
||||||
|
|
||||||
|
|
||||||
def json_formatter_cb(result, **kwargs):
|
def json_formatter_cb(result, **kwargs):
|
||||||
@ -129,7 +153,7 @@ def json_formatter_cb(result, **kwargs):
|
|||||||
|
|
||||||
@click.group(
|
@click.group(
|
||||||
invoke_without_command=True,
|
invoke_without_command=True,
|
||||||
cls=ExceptionHandlerGroup,
|
cls=CatchAllExceptions(click.Group),
|
||||||
result_callback=json_formatter_cb,
|
result_callback=json_formatter_cb,
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
|
@ -504,6 +504,7 @@ async def test_host_unsupported(unsupported_device_info):
|
|||||||
"foo",
|
"foo",
|
||||||
"--password",
|
"--password",
|
||||||
"bar",
|
"bar",
|
||||||
|
"--debug",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -563,6 +564,7 @@ async def test_host_auth_failed(discovery_mock, mocker):
|
|||||||
"foo",
|
"foo",
|
||||||
"--password",
|
"--password",
|
||||||
"bar",
|
"bar",
|
||||||
|
"--debug",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -610,3 +612,49 @@ async def test_shell(dev: Device, mocker):
|
|||||||
res = await runner.invoke(cli, ["shell"], obj=dev)
|
res = await runner.invoke(cli, ["shell"], obj=dev)
|
||||||
assert res.exit_code == 0
|
assert res.exit_code == 0
|
||||||
embed.assert_called()
|
embed.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_errors(mocker):
|
||||||
|
runner = CliRunner()
|
||||||
|
err = SmartDeviceException("Foobar")
|
||||||
|
|
||||||
|
# Test masking
|
||||||
|
mocker.patch("kasa.Discover.discover", side_effect=err)
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--username", "foo", "--password", "bar"],
|
||||||
|
)
|
||||||
|
assert res.exit_code == 1
|
||||||
|
assert "Raised error: Foobar" in res.output
|
||||||
|
assert "Run with --debug enabled to see stacktrace" in res.output
|
||||||
|
assert isinstance(res.exception, SystemExit)
|
||||||
|
|
||||||
|
# Test --debug
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--debug"],
|
||||||
|
)
|
||||||
|
assert res.exit_code == 1
|
||||||
|
assert "Raised error: Foobar" in res.output
|
||||||
|
assert res.exception == err
|
||||||
|
|
||||||
|
# Test no device passed to subcommand
|
||||||
|
mocker.patch("kasa.Discover.discover", return_value={})
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["sysinfo"],
|
||||||
|
)
|
||||||
|
assert res.exit_code == 1
|
||||||
|
assert (
|
||||||
|
"Raised error: Managed to invoke callback without a context object of type 'Device' existing."
|
||||||
|
in res.output
|
||||||
|
)
|
||||||
|
assert isinstance(res.exception, SystemExit)
|
||||||
|
|
||||||
|
# Test click error
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--foobar"],
|
||||||
|
)
|
||||||
|
assert res.exit_code == 2
|
||||||
|
assert "Raised error:" not in res.output
|
||||||
|
Loading…
Reference in New Issue
Block a user