Coverage for structured_tutorials / cli.py: 100%
39 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-21 19:08 +0100
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-21 19:08 +0100
1# Copyright (c) 2025 Mathias Ertl
2# Licensed under the MIT License. See LICENSE file for details.
4"""Main CLI entrypoint."""
6import argparse
7import sys
8from collections.abc import Sequence
9from pathlib import Path
11import yaml
13from structured_tutorials import __version__
14from structured_tutorials.errors import InvalidAlternativesSelectedError
15from structured_tutorials.models import TutorialModel
16from structured_tutorials.output import error, setup_logging
17from structured_tutorials.runners.local import LocalTutorialRunner
20def main(argv: Sequence[str] | None = None) -> None:
21 """Main entry function for the command-line."""
22 parser = argparse.ArgumentParser()
23 parser.add_argument("path", type=Path)
24 parser.add_argument("--version", action="version", version=__version__)
25 parser.add_argument("-a", "--alternative", dest="alternatives", action="append", default=[])
26 parser.add_argument("--no-colors", action="store_true", default=False)
27 parser.add_argument(
28 "-n",
29 "--non-interactive",
30 dest="interactive",
31 action="store_false",
32 default=True,
33 help="Never prompt for any user input.",
34 )
35 parser.add_argument(
36 "--log-level",
37 choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
38 default="INFO",
39 help="Override root log level",
40 )
41 parser.add_argument(
42 "--hide-commands",
43 dest="show_commands",
44 action="store_false",
45 default=True,
46 help="Do not show commands that are run by the tutorial.",
47 )
48 parser.add_argument(
49 "--hide-command-output",
50 dest="show_command_output",
51 action="store_false",
52 default=True,
53 help="Do not show the output of commands that are run on the terminal.",
54 )
55 args = parser.parse_args(argv)
57 setup_logging(level=args.log_level, no_colors=args.no_colors, show_commands=args.show_commands)
59 try:
60 tutorial = TutorialModel.from_file(args.path)
61 except yaml.YAMLError as exc: # an invalid YAML file
62 error(f"{args.path}: Invalid YAML file:")
63 print(exc, file=sys.stderr)
64 sys.exit(1)
65 except ValueError as ex: # thrown by Pydantic model loading
66 error(f"{args.path}: File is not a valid Tutorial:")
67 print(ex, file=sys.stderr)
68 sys.exit(1)
70 runner = LocalTutorialRunner(
71 tutorial,
72 alternatives=tuple(args.alternatives),
73 show_command_output=args.show_command_output,
74 interactive=args.interactive,
75 )
77 try:
78 runner.validate_alternatives()
79 except InvalidAlternativesSelectedError as ex:
80 error(str(ex))
81 sys.exit(1)
83 runner.run()