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