gencodo-py 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gencodo/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ """gencodo -- Generate CLI reference documentation from argparse-based applications."""
2
+
3
+ from gencodo._core import gen_docs, gen_docs_tree, get_bundled_templates
4
+ from gencodo._types import (
5
+ Command,
6
+ CommandGroup,
7
+ ExampleInfo,
8
+ FlagInfo,
9
+ TemplateInfo,
10
+ )
11
+
12
+ __all__ = [
13
+ "Command",
14
+ "CommandGroup",
15
+ "ExampleInfo",
16
+ "FlagInfo",
17
+ "TemplateInfo",
18
+ "gen_docs",
19
+ "gen_docs_tree",
20
+ "get_bundled_templates",
21
+ ]
22
+
23
+ __version__ = "0.1.0"
gencodo/_core.py ADDED
@@ -0,0 +1,287 @@
1
+ """Core documentation generation logic for gencodo."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import importlib.resources
7
+ import pathlib
8
+ import re
9
+ from collections.abc import Callable, Sequence
10
+ from typing import Literal, TextIO
11
+
12
+ from gencodo._jinja_env import _make_jinja_env
13
+ from gencodo._types import (
14
+ Command,
15
+ CommandGroup,
16
+ ExampleInfo,
17
+ FlagInfo,
18
+ TemplateInfo,
19
+ )
20
+
21
+ __all__ = [
22
+ "gen_docs",
23
+ "gen_docs_tree",
24
+ "get_bundled_templates",
25
+ ]
26
+
27
+
28
+ def _instantiate_command(command_class: type[Command]) -> Command:
29
+ """Instantiate a command class for parser introspection.
30
+
31
+ Tries command_class(None) first (craft_cli compatible),
32
+ then command_class() for simple classes.
33
+ """
34
+ try:
35
+ return command_class(None) # type: ignore[call-arg]
36
+ except TypeError:
37
+ return command_class() # type: ignore[call-arg]
38
+
39
+
40
+ def _extract_flags(command_class: type[Command]) -> list[FlagInfo]:
41
+ """Extract optional flags from a command class as FlagInfo objects.
42
+
43
+ Positional arguments and suppressed flags are excluded.
44
+ The longest option string is used as the flag name.
45
+
46
+ Args:
47
+ command_class: A command class satisfying the Command protocol.
48
+
49
+ Returns:
50
+ A list of FlagInfo objects for the command's optional flags.
51
+ """
52
+ parser = argparse.ArgumentParser(prog=command_class.name, add_help=False)
53
+ _instantiate_command(command_class).fill_parser(parser)
54
+ flags: list[FlagInfo] = []
55
+ for action in parser._actions:
56
+ if not action.option_strings:
57
+ continue
58
+ if action.help == argparse.SUPPRESS:
59
+ continue
60
+ name = max(action.option_strings, key=len)
61
+ usage = action.help or ""
62
+ if action.default is None or action.default is argparse.SUPPRESS:
63
+ default = ""
64
+ else:
65
+ default = str(action.default)
66
+ flags.append(FlagInfo(name=name, usage=usage, default_value=default))
67
+ return flags
68
+
69
+
70
+ def _infer_related(
71
+ command_class: type[Command],
72
+ command_groups: Sequence[CommandGroup],
73
+ ) -> list[str]:
74
+ """Determine the list of related command names for a command.
75
+
76
+ If the command has explicit ``related_commands``, those are validated
77
+ and returned. Otherwise, non-hidden siblings in the same group are returned.
78
+
79
+ Args:
80
+ command_class: The command class to find related commands for.
81
+ command_groups: All command groups in the application.
82
+
83
+ Returns:
84
+ A list of related command names.
85
+
86
+ Raises:
87
+ ValueError: If an explicit related command name is not found.
88
+ """
89
+ if command_class.related_commands is not None:
90
+ all_names = {c.name for g in command_groups for c in g.commands}
91
+ for name in command_class.related_commands:
92
+ if name not in all_names:
93
+ raise ValueError(
94
+ f"related command {name!r} not found in command_groups"
95
+ )
96
+ return list(command_class.related_commands)
97
+ for group in command_groups:
98
+ if command_class in group.commands:
99
+ siblings = [
100
+ c.name
101
+ for c in group.commands
102
+ if c is not command_class and not c.hidden
103
+ ]
104
+ return sorted(siblings)
105
+ return []
106
+
107
+
108
+ def _build_template_context(
109
+ command_class: type[Command],
110
+ appname: str,
111
+ command_groups: Sequence[CommandGroup],
112
+ ) -> dict[str, object]:
113
+ """Build the complete Jinja2 template context dict for a command.
114
+
115
+ Args:
116
+ command_class: The command class to build context for.
117
+ appname: The application name used in usage strings.
118
+ command_groups: All command groups in the application.
119
+
120
+ Returns:
121
+ A dict with keys: ref, command_name, short, long, synopsis,
122
+ examples, flags, related_commands, heading_len, appname.
123
+
124
+ Raises:
125
+ ValueError: If command_name or help_msg is empty.
126
+ """
127
+ command_name = command_class.name
128
+ short = command_class.help_msg
129
+ if not command_name:
130
+ raise ValueError("command_name must not be empty")
131
+ if not short:
132
+ raise ValueError("short (help_msg) must not be empty")
133
+
134
+ long = (command_class.overview or "").strip()
135
+
136
+ parser = argparse.ArgumentParser(
137
+ prog=f"{appname} {command_name}", add_help=False
138
+ )
139
+ _instantiate_command(command_class).fill_parser(parser)
140
+ raw_usage = parser.format_usage()
141
+ synopsis = re.sub(r"^usage:\s*", "", raw_usage).strip()
142
+
143
+ examples = [
144
+ ExampleInfo(info=info, usage=usage)
145
+ for info, usage in command_class.examples
146
+ ]
147
+
148
+ flags = _extract_flags(command_class)
149
+ related_commands = _infer_related(command_class, command_groups)
150
+ heading_len = len(command_name)
151
+ ref = command_name.replace("-", "_").replace(" ", "_")
152
+
153
+ return {
154
+ "ref": ref,
155
+ "command_name": command_name,
156
+ "short": short,
157
+ "long": long,
158
+ "synopsis": synopsis,
159
+ "examples": examples,
160
+ "flags": flags,
161
+ "related_commands": related_commands,
162
+ "heading_len": heading_len,
163
+ "appname": appname,
164
+ }
165
+
166
+
167
+ def gen_docs(
168
+ command_class: type[Command],
169
+ writer: TextIO,
170
+ template: str,
171
+ appname: str,
172
+ command_groups: Sequence[CommandGroup],
173
+ ) -> None:
174
+ """Render documentation for a single command to a writer.
175
+
176
+ Args:
177
+ command_class: The command class to document.
178
+ writer: A text stream to write the rendered output to.
179
+ template: A Jinja2 template string for the command page.
180
+ appname: The application name used in usage strings.
181
+ command_groups: All command groups (used for related commands).
182
+ """
183
+ env = _make_jinja_env()
184
+ compiled = env.from_string(template)
185
+ context = _build_template_context(command_class, appname, command_groups)
186
+ writer.write(compiled.render(context))
187
+
188
+
189
+ def gen_docs_tree(
190
+ appname: str,
191
+ command_groups: Sequence[CommandGroup],
192
+ output_dir: pathlib.Path,
193
+ templates: TemplateInfo,
194
+ file_prepender: Callable[[str], str] | None = None,
195
+ file_extension: str = ".md",
196
+ ) -> list[str]:
197
+ """Generate a documentation tree for all non-hidden commands.
198
+
199
+ Creates one file per command plus an index file in the output directory.
200
+
201
+ Args:
202
+ appname: The application name used in usage strings.
203
+ command_groups: All command groups in the application.
204
+ output_dir: Directory to write generated files into (created if needed).
205
+ templates: Template configuration for index and command pages.
206
+ file_prepender: Optional callable returning a string to prepend to each file.
207
+ file_extension: File extension for command pages (default: ".md").
208
+
209
+ Returns:
210
+ A list of generated command filenames (not including the index).
211
+ """
212
+ output_dir = pathlib.Path(output_dir)
213
+ output_dir.mkdir(parents=True, exist_ok=True)
214
+
215
+ env = _make_jinja_env()
216
+ compiled_cmd = env.from_string(templates.command_template)
217
+ compiled_idx = env.from_string(templates.index_template)
218
+
219
+ generated: list[str] = []
220
+ files_context: list[dict[str, str]] = []
221
+
222
+ for group in command_groups:
223
+ for cmd in group.commands:
224
+ if cmd.hidden:
225
+ continue
226
+ filename = cmd.name.replace(" ", "-") + file_extension
227
+ context = _build_template_context(cmd, appname, command_groups)
228
+ content = compiled_cmd.render(context)
229
+ if file_prepender is not None:
230
+ content = file_prepender(filename) + content
231
+ (output_dir / filename).write_text(content, encoding="utf-8")
232
+ generated.append(filename)
233
+ files_context.append(
234
+ {
235
+ "filename": filename,
236
+ "command_name": cmd.name,
237
+ "short": cmd.help_msg,
238
+ "group_name": group.name,
239
+ }
240
+ )
241
+
242
+ idx_content = compiled_idx.render(files=files_context, appname=appname)
243
+ if file_prepender is not None:
244
+ idx_content = file_prepender(templates.index_file_name) + idx_content
245
+ (output_dir / templates.index_file_name).write_text(
246
+ idx_content, encoding="utf-8"
247
+ )
248
+
249
+ return generated
250
+
251
+
252
+ def get_bundled_templates(
253
+ format: Literal["rst", "md"] = "rst",
254
+ index_file_name: str | None = None,
255
+ ) -> TemplateInfo:
256
+ """Load bundled default templates for the given format.
257
+
258
+ Args:
259
+ format: Template format -- "rst" for reStructuredText, "md" for Markdown.
260
+ index_file_name: Override the default index filename.
261
+ Defaults to "index.rst" or "index.md" based on format.
262
+
263
+ Returns:
264
+ A TemplateInfo with the bundled templates loaded.
265
+
266
+ Raises:
267
+ ValueError: If format is not "rst" or "md".
268
+ """
269
+ if format not in ("rst", "md"):
270
+ raise ValueError(f"format must be 'rst' or 'md', got {format!r}")
271
+
272
+ templates_pkg = importlib.resources.files("gencodo") / "templates" / format
273
+ command_template = (
274
+ (templates_pkg / f"command.{format}.j2").read_text(encoding="utf-8")
275
+ )
276
+ index_template = (
277
+ (templates_pkg / f"index.{format}.j2").read_text(encoding="utf-8")
278
+ )
279
+
280
+ if index_file_name is None:
281
+ index_file_name = f"index.{format}"
282
+
283
+ return TemplateInfo(
284
+ index_file_name=index_file_name,
285
+ index_template=index_template,
286
+ command_template=command_template,
287
+ )
gencodo/_jinja_env.py ADDED
@@ -0,0 +1,31 @@
1
+ """Jinja2 environment configuration for gencodo."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from jinja2 import Environment, StrictUndefined
6
+ from jinja2.filters import do_indent
7
+
8
+
9
+ def _make_jinja_env() -> Environment:
10
+ """Create a configured Jinja2 Environment for documentation generation.
11
+
12
+ Returns:
13
+ A Jinja2 Environment with custom filters for documentation rendering.
14
+ """
15
+ env = Environment(
16
+ autoescape=False,
17
+ undefined=StrictUndefined,
18
+ trim_blocks=True,
19
+ lstrip_blocks=True,
20
+ keep_trailing_newline=True,
21
+ )
22
+
23
+ def _indent(s: str, width: int = 4, *, blank: bool = False) -> str:
24
+ return do_indent(s, width=width, first=True, blank=blank)
25
+
26
+ def _repeat(s: str, n: int) -> str:
27
+ return s * n
28
+
29
+ env.filters["indent"] = _indent
30
+ env.filters["repeat"] = _repeat
31
+ return env
gencodo/_types.py ADDED
@@ -0,0 +1,92 @@
1
+ """Type definitions for gencodo: protocols, dataclasses, and named tuples."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import dataclasses
7
+ from collections.abc import Sequence
8
+ from typing import NamedTuple, Protocol, runtime_checkable
9
+
10
+ __all__ = [
11
+ "Command",
12
+ "CommandGroup",
13
+ "ExampleInfo",
14
+ "FlagInfo",
15
+ "TemplateInfo",
16
+ ]
17
+
18
+
19
+ @runtime_checkable
20
+ class Command(Protocol):
21
+ """Protocol that any CLI command class must satisfy.
22
+
23
+ Any class with these attributes and methods can be used with gencodo
24
+ via structural subtyping -- no inheritance required.
25
+ """
26
+
27
+ name: str
28
+ """The command name as invoked on the command line."""
29
+ help_msg: str
30
+ """Short one-line help string."""
31
+ overview: str
32
+ """Longer description of the command (may be multi-line)."""
33
+ hidden: bool
34
+ """Whether this command should be excluded from generated documentation."""
35
+ examples: list[tuple[str, str]]
36
+ """List of (description, command_string) example pairs."""
37
+ related_commands: list[str] | None
38
+ """Explicit list of related command names, or None to infer from siblings."""
39
+
40
+ def fill_parser(self, parser: argparse.ArgumentParser) -> None:
41
+ """Add command-specific arguments to the parser."""
42
+ ...
43
+
44
+
45
+ class CommandGroup(NamedTuple):
46
+ """A named group of commands."""
47
+
48
+ name: str
49
+ """Human-readable group name."""
50
+ commands: Sequence[type[Command]]
51
+ """Command classes in this group."""
52
+
53
+
54
+ @dataclasses.dataclass(frozen=True, slots=True, eq=True, order=False)
55
+ class ExampleInfo:
56
+ """Structured representation of a usage example."""
57
+
58
+ info: str
59
+ """Human-readable description of what the example demonstrates."""
60
+ usage: str
61
+ """The literal command string for the example."""
62
+
63
+
64
+ @dataclasses.dataclass(frozen=True, slots=True, eq=True, order=False)
65
+ class FlagInfo:
66
+ """Structured representation of a single CLI flag."""
67
+
68
+ name: str
69
+ """The flag name as it appears on the command line (e.g., '--verbose')."""
70
+ usage: str
71
+ """One-line description of the flag's purpose."""
72
+ default_value: str
73
+ """The default value shown in documentation."""
74
+
75
+
76
+ @dataclasses.dataclass(frozen=True, slots=True, eq=True, order=False)
77
+ class TemplateInfo:
78
+ """Jinja2 template configuration for documentation generation."""
79
+
80
+ index_file_name: str
81
+ """Output filename for the index document."""
82
+ index_template: str
83
+ """Jinja2 template string for the index document."""
84
+ command_template: str
85
+ """Jinja2 template string for per-command documents."""
86
+
87
+ def __post_init__(self) -> None:
88
+ """Validate that no field is empty or whitespace-only."""
89
+ for field_name in ("index_file_name", "index_template", "command_template"):
90
+ value = getattr(self, field_name)
91
+ if not value.strip():
92
+ raise ValueError(f"TemplateInfo.{field_name} must not be empty")
gencodo/py.typed ADDED
File without changes
@@ -0,0 +1,43 @@
1
+ # {{ command_name }}
2
+
3
+ {{ short }}
4
+
5
+ **Usage:**
6
+
7
+ ```
8
+ {{ synopsis }}
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ {{ long }}
14
+
15
+ {% if flags %}
16
+ ## Options
17
+
18
+ | Flag | Description | Default |
19
+ |------|-------------|---------|
20
+ {% for flag in flags %}
21
+ | `{{ flag.name }}` | {{ flag.usage }} | {% if flag.default_value %}`{{ flag.default_value }}`{% endif %} |
22
+ {% endfor %}
23
+
24
+ {% endif %}
25
+ {% if examples %}
26
+ ## Examples
27
+
28
+ {% for example in examples %}
29
+ **{{ example.info }}**
30
+
31
+ ```
32
+ {{ example.usage }}
33
+ ```
34
+
35
+ {% endfor %}
36
+ {% endif %}
37
+ {% if related_commands %}
38
+ ## See also
39
+
40
+ {% for cmd in related_commands %}
41
+ - [{{ cmd }}]({{ cmd }}.md)
42
+ {% endfor %}
43
+ {% endif %}
@@ -0,0 +1,25 @@
1
+ # CLI Reference
2
+
3
+ Command-line interface reference for **{{ appname }}**.
4
+
5
+ This reference documentation is automatically generated from the command
6
+ definitions and provides detailed information about each available command.
7
+
8
+ ## Available Commands
9
+
10
+ {% for group_name, group_files in files | groupby('group_name') %}
11
+ ### {{ group_name }}
12
+
13
+ {% for file in group_files %}
14
+ - [{{ file.command_name }}]({{ file.filename }}) -- {{ file.short }}
15
+ {% endfor %}
16
+
17
+ {% endfor %}
18
+
19
+ ## Quick Reference Table
20
+
21
+ | Command | Description |
22
+ |---------|-------------|
23
+ {% for file in files %}
24
+ | [{{ file.command_name }}]({{ file.filename }}) | {{ file.short }} |
25
+ {% endfor %}
@@ -0,0 +1,54 @@
1
+ .. _ref_{{ ref }}:
2
+
3
+ {{ command_name }}
4
+ {{ '=' | repeat(heading_len) }}
5
+
6
+ {{ short }}
7
+
8
+ **Usage:**
9
+
10
+ .. code-block:: bash
11
+
12
+ {{ synopsis }}
13
+
14
+ Overview
15
+ --------
16
+
17
+ {{ long }}
18
+
19
+ {% if flags %}
20
+ Options
21
+ -------
22
+
23
+ {% for flag in flags %}
24
+ .. option:: {{ flag.name }}
25
+
26
+ {{ flag.usage | indent(3) }}
27
+ {% if flag.default_value %}
28
+
29
+ Default: ``{{ flag.default_value }}``
30
+ {% endif %}
31
+
32
+ {% endfor %}
33
+ {% endif %}
34
+ {% if examples %}
35
+ Examples
36
+ --------
37
+
38
+ {% for example in examples %}
39
+ **{{ example.info }}**
40
+
41
+ .. code-block:: bash
42
+
43
+ {{ example.usage }}
44
+
45
+ {% endfor %}
46
+ {% endif %}
47
+ {% if related_commands %}
48
+ See also
49
+ --------
50
+
51
+ {% for cmd in related_commands %}
52
+ - :ref:`{{ cmd }} <ref_{{ cmd | replace('-', '_') | replace(' ', '_') }}>`
53
+ {% endfor %}
54
+ {% endif %}
@@ -0,0 +1,39 @@
1
+ .. _cli_reference:
2
+
3
+ CLI Reference
4
+ =============
5
+
6
+ Command-line interface reference for **{{ appname }}**.
7
+
8
+ This reference documentation is automatically generated from the command
9
+ definitions and provides detailed information about each available command.
10
+
11
+ Available Commands
12
+ ------------------
13
+
14
+ {% for group_name, group_files in files | groupby('group_name') %}
15
+ {{ group_name }}
16
+ {{ '~' | repeat(group_name | length) }}
17
+
18
+ .. toctree::
19
+ :maxdepth: 1
20
+
21
+ {% for file in group_files %}
22
+ {{ file.filename[:-4] }}
23
+ {% endfor %}
24
+
25
+ {% endfor %}
26
+
27
+ Quick Reference Table
28
+ ---------------------
29
+
30
+ .. list-table::
31
+ :header-rows: 1
32
+ :widths: 25 75
33
+
34
+ * - Command
35
+ - Description
36
+ {% for file in files %}
37
+ * - :ref:`{{ file.command_name }} <ref_{{ file.command_name | replace('-', '_') | replace(' ', '_') }}>`
38
+ - {{ file.short }}
39
+ {% endfor %}
@@ -0,0 +1,144 @@
1
+ Metadata-Version: 2.4
2
+ Name: gencodo-py
3
+ Version: 0.1.0
4
+ Summary: Generate CLI reference documentation from argparse-based applications using Jinja2 templates
5
+ Project-URL: Homepage, https://github.com/canonical/gencodo-py
6
+ Project-URL: Issues, https://github.com/canonical/gencodo-py/issues
7
+ Author: gencodo contributors
8
+ License-Expression: LGPL-3.0-only
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Documentation
18
+ Classifier: Topic :: Software Development :: Documentation
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: jinja2>=3.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest-cov; extra == 'dev'
25
+ Requires-Dist: pytest>=7.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # gencodo-py
29
+
30
+ Generate CLI reference documentation from argparse-based applications using Jinja2 templates.
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install gencodo-py
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ Define your CLI commands as plain Python classes (no base class required):
41
+
42
+ ```python
43
+ import argparse
44
+
45
+ class GreetCommand:
46
+ name = "greet"
47
+ help_msg = "Greet a specific person"
48
+ overview = "Personalize your greeting by specifying a name."
49
+ hidden = False
50
+ examples = [("Greet Alice", "myapp greet Alice")]
51
+ related_commands = None
52
+
53
+ def fill_parser(self, parser: argparse.ArgumentParser) -> None:
54
+ parser.add_argument("name", help="Name to greet")
55
+ parser.add_argument("--formal", action="store_true", default=False,
56
+ help="Use formal style")
57
+ ```
58
+
59
+ Generate documentation:
60
+
61
+ ```python
62
+ from gencodo import CommandGroup, gen_docs_tree, get_bundled_templates
63
+
64
+ groups = [CommandGroup(name="Greetings", commands=[GreetCommand])]
65
+ templates = get_bundled_templates("md") # or "rst"
66
+
67
+ gen_docs_tree(
68
+ appname="myapp",
69
+ command_groups=groups,
70
+ output_dir="docs/cli-ref",
71
+ templates=templates,
72
+ )
73
+ ```
74
+
75
+ ## API Reference
76
+
77
+ ### Types
78
+
79
+ - **`Command`** -- Protocol that any CLI command class must satisfy (structural subtyping).
80
+ - **`CommandGroup`** -- NamedTuple grouping commands under a name.
81
+ - **`ExampleInfo`** -- Dataclass for a usage example (info, usage).
82
+ - **`FlagInfo`** -- Dataclass for a CLI flag (name, usage, default_value).
83
+ - **`TemplateInfo`** -- Dataclass for Jinja2 template configuration.
84
+
85
+ ### Functions
86
+
87
+ - **`gen_docs(command_class, writer, template, appname, command_groups)`** -- Render docs for a single command to a TextIO writer.
88
+ - **`gen_docs_tree(appname, command_groups, output_dir, templates, ...)`** -- Generate a full documentation tree (one file per command + index).
89
+ - **`get_bundled_templates(format="rst", index_file_name=None)`** -- Load bundled reST or Markdown templates.
90
+
91
+ ### Command Protocol
92
+
93
+ Your command classes need these attributes/methods:
94
+
95
+ | Attribute | Type | Description |
96
+ |-----------|------|-------------|
97
+ | `name` | `str` | Command name |
98
+ | `help_msg` | `str` | Short help string |
99
+ | `overview` | `str` | Longer description |
100
+ | `hidden` | `bool` | Exclude from docs if True |
101
+ | `examples` | `list[tuple[str, str]]` | (description, command) pairs |
102
+ | `related_commands` | `list[str] \| None` | Explicit related commands, or None to auto-infer |
103
+ | `fill_parser(parser)` | method | Add arguments to an ArgumentParser |
104
+
105
+ ## Template Customization
106
+
107
+ ### Bundled Templates
108
+
109
+ Use `get_bundled_templates("rst")` or `get_bundled_templates("md")` for the built-in templates.
110
+
111
+ ### Custom Templates
112
+
113
+ Pass your own Jinja2 template strings via `TemplateInfo`:
114
+
115
+ ```python
116
+ from gencodo import TemplateInfo
117
+
118
+ templates = TemplateInfo(
119
+ index_file_name="index.md",
120
+ index_template="# Commands\n{% for f in files %}...\n{% endfor %}",
121
+ command_template="# {{ command_name }}\n{{ short }}\n...",
122
+ )
123
+ ```
124
+
125
+ Available template variables for command templates:
126
+
127
+ | Variable | Type | Description |
128
+ |----------|------|-------------|
129
+ | `ref` | `str` | Anchor reference (underscored name) |
130
+ | `command_name` | `str` | Command name |
131
+ | `short` | `str` | Short help message |
132
+ | `long` | `str` | Overview text |
133
+ | `synopsis` | `str` | Usage synopsis |
134
+ | `examples` | `list[ExampleInfo]` | Usage examples |
135
+ | `flags` | `list[FlagInfo]` | Optional flags |
136
+ | `related_commands` | `list[str]` | Related command names |
137
+ | `heading_len` | `int` | Length of command name |
138
+ | `appname` | `str` | Application name |
139
+
140
+ Custom Jinja2 filters available: `indent(width)`, `repeat(n)`.
141
+
142
+ ## License
143
+
144
+ LGPL-3.0-only
@@ -0,0 +1,12 @@
1
+ gencodo/__init__.py,sha256=jn-eancNlIWTvin_HqfjREHb8VzTHpwxnEG5j8z467k,467
2
+ gencodo/_core.py,sha256=fx0HS35Ur20lHhC7g6gSHtYBmoB6ma91xAgefuGykOc,9421
3
+ gencodo/_jinja_env.py,sha256=GadUxPluIB5y_G2VVnGZ_SpVTN7PGc9hf9Suat5DJos,859
4
+ gencodo/_types.py,sha256=piDxKr0zanskQGmTV-Il6EKxGuvYeKBkTab41__EXtA,2907
5
+ gencodo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ gencodo/templates/md/command.md.j2,sha256=7lC23VA5hnotciY1FLClctMYqCQNqjbHtV80fWA-G0o,612
7
+ gencodo/templates/md/index.md.j2,sha256=e9_2YZilPpj9JTcx4d5yu0tiJI8Ro-tetCo_GBlzm4I,648
8
+ gencodo/templates/rst/command.rst.j2,sha256=agRAQdnSRGW3kR5QjWmWgcBeNHsCHziFI2O44tFfd38,741
9
+ gencodo/templates/rst/index.rst.j2,sha256=iUGoAPRwzdHnZob-Gl1j6XnKVVkZEmR5cXJfdziPXu4,838
10
+ gencodo_py-0.1.0.dist-info/METADATA,sha256=RqQF2hKFTsMIMuri35r6_twek8YSVX5jalvezLf2ero,4821
11
+ gencodo_py-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
12
+ gencodo_py-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any