Coverage for src/somesy/cli/util.py: 75%
36 statements
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-30 09:42 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-30 09:42 +0000
1"""Utility functions for CLI commands."""
2import logging
3import traceback
4from typing import Optional
6import typer
7import wrapt
8from rich.markup import escape
9from rich.pretty import pretty_repr
11from somesy.core.core import discover_input
12from somesy.core.log import SomesyLogLevel, get_log_level, set_log_level
13from somesy.core.models import SomesyConfig, SomesyInput
15logger = logging.getLogger("somesy")
18# configuration dicts for CLI file arguments
19file_arg_config = dict(
20 file_okay=True,
21 dir_okay=False,
22 writable=True,
23 readable=True,
24 resolve_path=True,
25)
26existing_file_arg_config = dict(file_arg_config)
27existing_file_arg_config.update(dict(exists=True))
30@wrapt.decorator
31def wrap_exceptions(wrapped, instance, args, kwargs):
32 """Format and log exceptions for cli commands."""
33 try:
34 return wrapped(*args, **kwargs)
36 except Exception as e:
37 # Escape the error message to prevent Rich from misinterpreting it
38 escaped_error_message = escape(str(e))
39 escaped_traceback = escape(traceback.format_exc())
41 logger.error(f"[bold red]Error: {escaped_error_message}[/bold red]")
42 logger.debug(f"[red]{escaped_traceback}[/red]")
43 raise typer.Exit(code=1) from e
46def resolved_somesy_input(**cli_args) -> SomesyInput:
47 """Return a combined `SomesyInput` based on config file and passed CLI args.
49 Will also adjust log levels accordingly.
50 """
51 # figure out what input file to use
52 input_file = discover_input(cli_args.pop("input_file", None))
54 # create config based on passed arguments
55 passed_args = {k: v for k, v in cli_args.items() if v is not None}
56 somesy_conf = SomesyConfig(input_file=input_file, **passed_args)
58 # cli_log_level is None if the user did not pass a log level (-> "default")
59 cli_log_level: Optional[SomesyLogLevel] = get_log_level()
61 if cli_log_level is not None:
62 # update log level flags if cli log level was set
63 somesy_conf.update_log_level(cli_log_level)
65 somesy_input: SomesyInput = somesy_conf.get_input()
67 if cli_log_level is None:
68 # no cli log level -> set it according to the loaded configuration
69 set_log_level(somesy_input.config.log_level())
71 logger.debug(
72 f"Combined config (Defaults + File + CLI):\n{pretty_repr(somesy_input.config)}"
73 )
74 return somesy_input