1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
# Copyright 2022 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Code for auto-generating README.md."""
import argparse
import io
class MarkdownHelpFormatter(argparse.HelpFormatter):
"""Callbacks to format help output as Markdown."""
def __init__(self, prog):
self._prog = prog
self._section_title = None
self._section_contents = []
self._paragraphs = []
super().__init__(prog=prog)
def add_text(self, text):
if text and text is not argparse.SUPPRESS:
lst = self._paragraphs
if self._section_title:
lst = self._section_contents
lst.append(text)
def start_section(self, heading):
self._section_title = heading.title()
self._section_contents = []
def end_section(self):
if self._section_contents:
self._paragraphs.append(f"#### {self._section_title}")
self._paragraphs.extend(self._section_contents)
self._section_title = None
def add_usage(self, usage, actions, groups, prefix=None):
if not usage:
usage = self._prog
self.add_text(
f"**Usage:** `{usage} {self._format_actions_usage(actions, groups)}`"
)
def add_arguments(self, actions):
def _get_metavar(action):
return action.metavar or action.dest
def _format_invocation(action):
if action.option_strings:
parts = []
for option_string in action.option_strings:
if action.nargs == 0:
parts.append(option_string)
else:
parts.append(
f"{option_string} {_get_metavar(action).upper()}"
)
return ", ".join(f"`{part}`" for part in parts)
return f"`{_get_metavar(action)}`"
def _get_table_line(action):
return f"| {_format_invocation(action)} | {action.help} |"
table_lines = [
"| | |",
"|---|---|",
*(
_get_table_line(action)
for action in actions
if action.help is not argparse.SUPPRESS
),
]
# Don't want a table with no rows.
if len(table_lines) > 2:
self.add_text("\n".join(table_lines))
def format_help(self):
return "\n\n".join(self._paragraphs)
def generate_readme():
"""Generate the README.md file.
Returns:
A string with the README contents.
"""
# Deferred import position to avoid circular dependency.
# Normally, this would not be required, since we don't use from
# imports. But runpy's import machinery essentially does the
# equivalent of a from import on __main__.py.
import zmake.__main__ # pylint: disable=import-outside-toplevel
output = io.StringIO()
parser, sub_action = zmake.__main__.get_argparser()
def _append(*args, **kwargs):
kwargs.setdefault("file", output)
print(*args, **kwargs)
def _append_argparse_help(parser):
parser.formatter_class = MarkdownHelpFormatter
_append(parser.format_help())
_append("# Zmake")
_append()
_append(
'<!-- Auto-generated contents! Run "zmake generate-readme" to update. -->'
)
_append()
_append("[TOC]")
_append()
_append("## Usage")
_append()
_append_argparse_help(parser)
_append()
_append("## Subcommands")
for sub_name, sub_parser in sub_action.choices.items():
_append()
_append(f"### zmake {sub_name}")
_append()
_append_argparse_help(sub_parser)
return output.getvalue()
|