StrictDoc Documentation
strictdoc/cli/cli_arg_parser.py
Source file coverage
Path:
strictdoc/cli/cli_arg_parser.py
Lines:
118
Non-empty lines:
100
Non-empty lines covered with requirements:
100 / 100 (100.0%)
Functions:
9
Functions covered by requirements:
9 / 9 (100.0%)
1
import argparse
2
import sys
3
from typing import Any, Dict, NoReturn
4
 
5
from strictdoc import __version__
6
from strictdoc.helpers.cast import assert_cast
7
from strictdoc.helpers.parallelizer import Parallelizer
8
 
9
 
10
def formatter(prog: str) -> argparse.RawTextHelpFormatter:
11
    return argparse.RawTextHelpFormatter(
12
        prog, indent_increment=2, max_help_position=4, width=80
13
    )
14
 
15
 
16
class SDocArgumentParser(argparse.ArgumentParser):
17
    def error(self, message: str) -> NoReturn:
18
        self.print_usage(sys.stderr)
19
        print(f"{self.prog}: error: {message}", file=sys.stderr)  # noqa: T201
20
        print("")  # noqa: T201
21
        print("Further help:")  # noqa: T201
22
        print(  # noqa: T201
23
            "'strictdoc -h/--help' provides a general overview of available commands."
24
        )
25
        print(  # noqa: T201
26
            "'strictdoc <command> -h/--help' provides command-specific help."
27
        )
28
        sys.exit(2)
29
 
30
 
31
class SDocArgsParser:
32
    @classmethod
33
    def create_sdoc_args_parser(
34
        cls, registry: Dict[str, Any]
35
    ) -> "SDocArgsParser":
36
        parser = cls.build_argparse(registry)
37
        args = parser.parse_args()
38
        return cls(args, registry)
39
 
40
    def __init__(self, args: argparse.Namespace, registry: Dict[str, Any]):
41
        self.args: argparse.Namespace = args
42
        self.registry: Dict[str, Any] = registry
43
 
44
    def is_debug_mode(self) -> bool:
45
        return assert_cast(self.args.debug, bool)
46
 
47
    def run(self, parallelizer: Parallelizer) -> bool:
48
        if self.args.command not in self.registry:
49
            return False
50
 
51
        cmd = self.registry[self.args.command]
52
        if isinstance(cmd, dict):
53
            assert self.args.subcommand in cmd
54
            command_instance = cmd[self.args.subcommand](self.args)
55
        else:
56
            command_instance = cmd(self.args)
57
        command_instance.run(parallelizer)
58
 
59
        return True
60
 
61
    @classmethod
62
    def build_argparse(cls, registry: Dict[str, Any]) -> SDocArgumentParser:
63
        # https://stackoverflow.com/a/19476216/598057
64
        main_parser = SDocArgumentParser(
65
            prog="strictdoc",
66
            add_help=True,
67
            epilog=(
68
                """
69
                Further help: https://strictdoc.readthedocs.io/en/stable/
70
                """
71
            ),
72
        )
73
 
74
        # The -v/--version has a special behavior that it still works when all
75
        # commands are required == True.
76
        # https://stackoverflow.com/a/12123598/598057
77
        main_parser.add_argument(
78
            "-v", "--version", action="version", version=__version__
79
        )
80
 
81
        main_parser.add_argument(
82
            "--debug",
83
            action="store_true",
84
            default=False,
85
            help="Enable more verbose printing of errors when they are encountered.",
86
        )
87
 
88
        command_subparsers = main_parser.add_subparsers(
89
            title="command", dest="command"
90
        )
91
        command_subparsers.required = True
92
 
93
        # Dynamically add subcommands
94
        for name, cmd in registry.items():
95
            if isinstance(cmd, dict):  # command family
96
                family_parser = command_subparsers.add_parser(name)
97
                family_subparsers = family_parser.add_subparsers(
98
                    dest="subcommand"
99
                )
100
                family_subparsers.required = True
101
                for subname, subcmd in cmd.items():
102
                    sub_parser = family_subparsers.add_parser(
103
                        subname,
104
                        help=subcmd.HELP,
105
                        description=subcmd.DETAILED_HELP,
106
                        formatter_class=formatter,
107
                    )
108
                    subcmd.add_arguments(sub_parser)
109
            else:
110
                cmd_parser = command_subparsers.add_parser(
111
                    name,
112
                    help=cmd.HELP,
113
                    description=cmd.DETAILED_HELP,
114
                    formatter_class=formatter,
115
                )
116
                cmd.add_arguments(cmd_parser)
117
 
118
        return main_parser