Path:
strictdoc/cli/cli_arg_parser.py
Lines:
134
Non-empty lines:
114
Non-empty lines covered with requirements:
114 / 114 (100.0%)
Functions:
10
Functions covered by requirements:
10 / 10 (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
@classmethod33
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 is_development_mode(self) -> bool:
48
return assert_cast(self.args.development, bool)
49
50
def run(self, parallelizer: Parallelizer) -> bool:
51
if self.args.command not in self.registry:
52
return False
53
54
cmd = self.registry[self.args.command]
55
if isinstance(cmd, dict):
56
assert self.args.subcommand in cmd
57
command_instance = cmd[self.args.subcommand](self.args)
58
else:
59
command_instance = cmd(self.args)
60
command_instance.run(parallelizer)
61
62
return True
63
64
@classmethod65
def build_argparse(cls, registry: Dict[str, Any]) -> SDocArgumentParser:
66
# https://stackoverflow.com/a/19476216/59805767
main_parser = SDocArgumentParser(
68
prog="strictdoc",
69
add_help=True,
70
epilog=(
71
"""
72
Further help: https://strictdoc.readthedocs.io/en/stable/73
"""74
),75
)76
77
# The -v/--version has a special behavior that it still works when all78
# commands are required == True.79
# https://stackoverflow.com/a/12123598/59805780
main_parser.add_argument(
81
"-v", "--version", action="version", version=__version__
82
)83
84
main_parser.add_argument(
85
"--debug",
86
action="store_true",
87
default=False,
88
help="Enable more verbose printing of errors when they are encountered.",
89
)90
91
# Marks a StrictDoc-team-internal development instance (e.g. our own92
# `invoke server`), independent of --debug: --debug is about verbose93
# error output and is meaningful for end users too, while this flag94
# only identifies "this is StrictDoc's own dev server" (see95
# developer/tasks/20260708_favicon_vars/task.md for the favicon use96
# case). Hidden from --help on purpose, not for end users.97
main_parser.add_argument(
98
"--development",
99
action="store_true",
100
default=False,
101
help=argparse.SUPPRESS,
102
)103
104
command_subparsers = main_parser.add_subparsers(
105
title="command", dest="command"
106
)107
command_subparsers.required = True
108
109
# Dynamically add subcommands110
for name, cmd in registry.items():
111
if isinstance(cmd, dict): # command family
112
family_parser = command_subparsers.add_parser(name)
113
family_subparsers = family_parser.add_subparsers(
114
dest="subcommand"
115
)116
family_subparsers.required = True
117
for subname, subcmd in cmd.items():
118
sub_parser = family_subparsers.add_parser(
119
subname,
120
help=subcmd.HELP,
121
description=subcmd.DETAILED_HELP,
122
formatter_class=formatter,
123
)124
subcmd.add_arguments(sub_parser)
125
else:
126
cmd_parser = command_subparsers.add_parser(
127
name,
128
help=cmd.HELP,
129
description=cmd.DETAILED_HELP,
130
formatter_class=formatter,
131
)132
cmd.add_arguments(cmd_parser)
133
134
return main_parser