PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions | Variables
generate_cli_reference Namespace Reference

Functions

 build_parser ()
 Build the real parser, including registrars delegated from other modules.
 
str escape (text)
 Escape text for generated HTML.
 
dict describe_action (action)
 Capture the user-relevant contract of one argparse action.
 
str _normalize_group_title (title)
 Resolve one argparse group title to a Python-version-independent form.
 
dict subcommands (parser)
 The subparser map of a parser, if it has one.
 
dict describe_parser (str name, parser)
 Recursively capture a command and its subcommands.
 
dict snapshot ()
 The full normalized parser snapshot.
 
list render_command (dict command, int depth=0, str prefix="")
 Render one command and its subcommands as HTML.
 
str render (dict data)
 Render the whole reference as an includable HTML fragment.
 
int main ()
 Write or verify the generated CLI reference.
 

Variables

 REPO_ROOT = Path(__file__).resolve().parents[2]
 
str GENERATED_DIR = REPO_ROOT / "docs" / "generated"
 
str HTML_PATH = GENERATED_DIR / "cli_reference.html"
 
str JSON_PATH = GENERATED_DIR / "cli_reference.json"
 
dict _MACHINE_DEPENDENT_DEFAULTS = {"workers": "<local CPU count, capped 1-8>"}
 
 _DEFAULT_GROUP_TITLE_SPELLINGS = frozenset({"optional arguments", "options"})
 
str _CANONICAL_DEFAULT_GROUP_TITLE = "options"
 

Detailed Description

Generate the CLI reference by introspecting the fully assembled argparse parser.

Function Documentation

◆ build_parser()

generate_cli_reference.build_parser ( )

Build the real parser, including registrars delegated from other modules.

Regex over cli.py would miss add_storage_parser(), which contributes a whole top-level command from storage.py. Only the assembled parser knows the true command set.

Returns
The assembled argparse parser.

Definition at line 29 of file generate_cli_reference.py.

29def build_parser():
30 """!
31 @brief Build the real parser, including registrars delegated from other modules.
32
33 @details Regex over `cli.py` would miss `add_storage_parser()`, which contributes a
34 whole top-level command from `storage.py`. Only the assembled parser knows
35 the true command set.
36 @return The assembled argparse parser.
37 """
38 sys.path.insert(0, str(REPO_ROOT))
39 from picurv_cli.cli import build_main_parser
40
41 return build_main_parser()
42
43
Here is the caller graph for this function:

◆ escape()

str generate_cli_reference.escape (   text)

Escape text for generated HTML.

Parameters
[in]textValue to escape.
Returns
Escaped string.

Definition at line 44 of file generate_cli_reference.py.

44def escape(text) -> str:
45 """!
46 @brief Escape text for generated HTML.
47 @param[in] text Value to escape.
48 @return Escaped string.
49 """
50 return (
51 str(text).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
52 )
53
54

◆ describe_action()

dict generate_cli_reference.describe_action (   action)

Capture the user-relevant contract of one argparse action.

Parameters
[in]actionParser action.
Returns
Mapping describing the action.

Definition at line 55 of file generate_cli_reference.py.

55def describe_action(action) -> dict:
56 """!
57 @brief Capture the user-relevant contract of one argparse action.
58 @param[in] action Parser action.
59 @return Mapping describing the action.
60 """
61 if action.dest in _MACHINE_DEPENDENT_DEFAULTS and action.default not in (None, argparse.SUPPRESS):
62 default = _MACHINE_DEPENDENT_DEFAULTS[action.dest]
63 elif action.default in (None, argparse.SUPPRESS):
64 default = None
65 else:
66 default = str(action.default)
67 return {
68 "flags": list(action.option_strings),
69 "dest": action.dest,
70 "required": bool(getattr(action, "required", False)),
71 "choices": sorted(str(choice) for choice in action.choices) if action.choices else [],
72 "default": default,
73 "nargs": None if action.nargs is None else str(action.nargs),
74 "help": (action.help or "").strip(),
75 "is_flag": isinstance(action, (argparse._StoreTrueAction, argparse._StoreFalseAction)),
76 }
77
78
79#: argparse's own title for the group of options nobody explicitly grouped. Python
80#: renamed it from "optional arguments" to "options" in 3.10 (bpo-9694); a group's
81#: title is otherwise a literal string this repo wrote, so only this one default
82#: needs normalizing. Left un-normalized, the generated reference would flip between
83#: spellings depending on which Python happened to run the generator, making `--check`
84#: fail on any interpreter that disagrees with whichever one last regenerated the file.
Head of a generic C-style linked list.
Definition variables.h:475
Here is the caller graph for this function:

◆ _normalize_group_title()

str generate_cli_reference._normalize_group_title (   title)
protected

Resolve one argparse group title to a Python-version-independent form.

Parameters
[in]titleRaw group.title from argparse, or None for the positional group.
Returns
Canonical title.

Definition at line 89 of file generate_cli_reference.py.

89def _normalize_group_title(title) -> str:
90 """!
91 @brief Resolve one argparse group title to a Python-version-independent form.
92 @param[in] title Raw `group.title` from argparse, or None for the positional group.
93 @return Canonical title.
94 """
95 if title in _DEFAULT_GROUP_TITLE_SPELLINGS:
96 return _CANONICAL_DEFAULT_GROUP_TITLE
97 return title or "arguments"
98
99
Here is the caller graph for this function:

◆ subcommands()

dict generate_cli_reference.subcommands (   parser)

The subparser map of a parser, if it has one.

Parameters
[in]parserParser to inspect.
Returns
Mapping of command name to subparser.

Definition at line 100 of file generate_cli_reference.py.

100def subcommands(parser) -> dict:
101 """!
102 @brief The subparser map of a parser, if it has one.
103 @param[in] parser Parser to inspect.
104 @return Mapping of command name to subparser.
105 """
106 for action in parser._actions:
107 if isinstance(action, argparse._SubParsersAction):
108 return dict(action.choices)
109 return {}
110
111
Here is the caller graph for this function:

◆ describe_parser()

dict generate_cli_reference.describe_parser ( str  name,
  parser 
)

Recursively capture a command and its subcommands.

Parameters
[in]nameCommand name.
[in]parserParser for that command.
Returns
Nested command description.

Definition at line 112 of file generate_cli_reference.py.

112def describe_parser(name: str, parser) -> dict:
113 """!
114 @brief Recursively capture a command and its subcommands.
115 @param[in] name Command name.
116 @param[in] parser Parser for that command.
117 @return Nested command description.
118 """
119 groups: dict = {}
120 for group in parser._action_groups:
121 actions = [
122 describe_action(action)
123 for action in group._group_actions
124 if not isinstance(action, argparse._SubParsersAction)
125 and action.dest != argparse.SUPPRESS
126 ]
127 if actions:
128 groups.setdefault(_normalize_group_title(group.title), []).extend(actions)
129 children = {
130 child: describe_parser(child, child_parser)
131 for child, child_parser in sorted(subcommands(parser).items())
132 }
133 return {
134 "name": name,
135 "help": (parser.description or "").strip().splitlines()[0] if parser.description else "",
136 "groups": groups,
137 "subcommands": children,
138 }
139
140
Here is the call graph for this function:
Here is the caller graph for this function:

◆ snapshot()

dict generate_cli_reference.snapshot ( )

The full normalized parser snapshot.

Returns
Snapshot mapping.

Definition at line 141 of file generate_cli_reference.py.

141def snapshot() -> dict:
142 """!
143 @brief The full normalized parser snapshot.
144 @return Snapshot mapping.
145 """
146 parser = build_parser()
147 return {
148 "program": parser.prog,
149 "commands": {
150 name: describe_parser(name, sub)
151 for name, sub in sorted(subcommands(parser).items())
152 },
153 }
154
155
Here is the call graph for this function:
Here is the caller graph for this function:

◆ render_command()

list generate_cli_reference.render_command ( dict  command,
int   depth = 0,
str   prefix = "" 
)

Render one command and its subcommands as HTML.

A subcommand heading carries its full invocation path. Rendering status on its own would sit indistinguishably beside the top-level status-source, and a reader scanning headings could not tell which command a flag table belongs to.

Parameters
[in]commandCommand description.
[in]depthNesting depth.
[in]prefixInvocation path of the parent command, empty at the top level.
Returns
HTML lines.

Definition at line 156 of file generate_cli_reference.py.

156def render_command(command: dict, depth: int = 0, prefix: str = "") -> list:
157 """!
158 @brief Render one command and its subcommands as HTML.
159
160 @details A subcommand heading carries its full invocation path. Rendering `status`
161 on its own would sit indistinguishably beside the top-level
162 `status-source`, and a reader scanning headings could not tell which
163 command a flag table belongs to.
164 @param[in] command Command description.
165 @param[in] depth Nesting depth.
166 @param[in] prefix Invocation path of the parent command, empty at the top level.
167 @return HTML lines.
168 """
169 heading = "h3" if depth == 0 else "h4"
170 invocation = f"{prefix}{command['name']}"
171 slug = invocation.replace("-", "_").replace(" ", "_")
172 out = [f'<{heading} id="cli_{slug}"><code>picurv {escape(invocation)}</code></{heading}>']
173 if command["help"]:
174 out.append(f"<p>{escape(command['help'])}</p>")
175 for title, actions in command["groups"].items():
176 if not actions:
177 continue
178 out.append(f"<p><b>{escape(title)}</b></p>")
179 out.append('<table class="markdownTable"><tr>'
180 '<th class="markdownTableHeadNone">Flag</th>'
181 '<th class="markdownTableHeadNone">Required</th>'
182 '<th class="markdownTableHeadNone">Choices</th>'
183 '<th class="markdownTableHeadNone">Default</th>'
184 '<th class="markdownTableHeadNone">Description</th></tr>')
185 for action in actions:
186 flags = ", ".join(f"<code>{escape(flag)}</code>" for flag in action["flags"]) or \
187 f"<code>{escape(action['dest'])}</code> (positional)"
188 choices = ", ".join(f"<code>{escape(c)}</code>" for c in action["choices"]) or "&#8212;"
189 default = f"<code>{escape(action['default'])}</code>" if action["default"] else "&#8212;"
190 out.append(
191 "<tr>"
192 f'<td class="markdownTableBodyNone">{flags}</td>'
193 f'<td class="markdownTableBodyNone">{"yes" if action["required"] else "no"}</td>'
194 f'<td class="markdownTableBodyNone">{choices}</td>'
195 f'<td class="markdownTableBodyNone">{default}</td>'
196 f'<td class="markdownTableBodyNone">{escape(action["help"])}</td>'
197 "</tr>"
198 )
199 out.append("</table>")
200 for child in command["subcommands"].values():
201 out += render_command(child, depth + 1, f"{invocation} ")
202 return out
203
204
Here is the call graph for this function:
Here is the caller graph for this function:

◆ render()

str generate_cli_reference.render ( dict  data)

Render the whole reference as an includable HTML fragment.

Parameters
[in]dataParser snapshot.
Returns
HTML text.

Definition at line 205 of file generate_cli_reference.py.

205def render(data: dict) -> str:
206 """!
207 @brief Render the whole reference as an includable HTML fragment.
208 @param[in] data Parser snapshot.
209 @return HTML text.
210 """
211 out = [
212 "<!-- GENERATED FILE - do not edit by hand.",
213 " Source of truth: the assembled argparse parser (picurv_cli.cli.build_main_parser),",
214 " including registrars delegated from other modules such as storage.py.",
215 " Regenerate with: make docs-cli-reference -->",
216 f"<p>{len(data['commands'])} top-level commands.</p>",
217 ]
218 for command in data["commands"].values():
219 out += render_command(command)
220 return "\n".join(out) + "\n"
221
222
Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int generate_cli_reference.main ( )

Write or verify the generated CLI reference.

Returns
Process status code.

Definition at line 223 of file generate_cli_reference.py.

223def main() -> int:
224 """!
225 @brief Write or verify the generated CLI reference.
226 @return Process status code.
227 """
228 parser = argparse.ArgumentParser(description="Generate the CLI reference from the live parser.")
229 parser.add_argument("--check", action="store_true", help="Fail if the generated output is stale.")
230 args = parser.parse_args()
231
232 data = snapshot()
233 payload = json.dumps(data, indent=2, sort_keys=True) + "\n"
234 html = render(data)
235
236 if args.check:
237 stale = [
238 path.relative_to(REPO_ROOT)
239 for path, content in ((JSON_PATH, payload), (HTML_PATH, html))
240 if not path.is_file() or path.read_text(encoding="utf-8") != content
241 ]
242 if stale:
243 print("Generated CLI reference is stale:", file=sys.stderr)
244 for path in stale:
245 print(f" {path}", file=sys.stderr)
246 print("\nThe parser changed. Regenerate with: make docs-cli-reference", file=sys.stderr)
247 return 1
248 print(f"CLI reference is current ({len(data['commands'])} commands).")
249 return 0
250
251 GENERATED_DIR.mkdir(parents=True, exist_ok=True)
252 JSON_PATH.write_text(payload, encoding="utf-8")
253 HTML_PATH.write_text(html, encoding="utf-8")
254 total = sum(1 for _ in data["commands"])
255 subs = sum(len(c["subcommands"]) for c in data["commands"].values())
256 print(f"Wrote CLI reference: {total} commands, {subs} subcommands.")
257 return 0
258
259
int main(int argc, char **argv)
Entry point for the postprocessor executable.
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ REPO_ROOT

generate_cli_reference.REPO_ROOT = Path(__file__).resolve().parents[2]

Definition at line 12 of file generate_cli_reference.py.

◆ GENERATED_DIR

str generate_cli_reference.GENERATED_DIR = REPO_ROOT / "docs" / "generated"

Definition at line 13 of file generate_cli_reference.py.

◆ HTML_PATH

str generate_cli_reference.HTML_PATH = GENERATED_DIR / "cli_reference.html"

Definition at line 14 of file generate_cli_reference.py.

◆ JSON_PATH

str generate_cli_reference.JSON_PATH = GENERATED_DIR / "cli_reference.json"

Definition at line 15 of file generate_cli_reference.py.

◆ _MACHINE_DEPENDENT_DEFAULTS

dict generate_cli_reference._MACHINE_DEPENDENT_DEFAULTS = {"workers": "<local CPU count, capped 1-8>"}
protected

Definition at line 26 of file generate_cli_reference.py.

◆ _DEFAULT_GROUP_TITLE_SPELLINGS

generate_cli_reference._DEFAULT_GROUP_TITLE_SPELLINGS = frozenset({"optional arguments", "options"})
protected

Definition at line 85 of file generate_cli_reference.py.

◆ _CANONICAL_DEFAULT_GROUP_TITLE

str generate_cli_reference._CANONICAL_DEFAULT_GROUP_TITLE = "options"
protected

Definition at line 86 of file generate_cli_reference.py.