Skip to main content

gencodo-py

Generate CLI reference documentation from argparse-based applications using Jinja2 templates.

gencodo-py is the Python sibling of gencodo (Go, for Cobra). It extracts structured data from command classes (description, usage, positional arguments, options, examples, related commands) and renders it through templates you control, so the output can be reStructuredText, Markdown, or any other text format. Command classes only need a few attributes and a fill_parser method; craft-cli commands satisfy the protocol as they are.

Installation

pip install gencodo-py

Quick Start

Define your CLI commands as plain Python classes (no base class required):

import argparse

class GreetCommand:
    name = "greet"
    help_msg = "Greet a specific person"
    overview = "Personalize your greeting by specifying a name."
    hidden = False
    examples = [("Greet Alice", "myapp greet Alice")]
    related_commands = None

    def fill_parser(self, parser: argparse.ArgumentParser) -> None:
        parser.add_argument("name", help="Name to greet")
        parser.add_argument("-f", "--formal", action="store_true", help="Use formal style")

Generate documentation:

from gencodo import CommandGroup, gen_docs_tree, get_bundled_templates

groups = [CommandGroup(name="Greetings", commands=[GreetCommand])]
templates = get_bundled_templates("md")  # or "rst"

gen_docs_tree(
    appname="myapp",
    command_groups=groups,
    output_dir="docs/cli-ref",
    templates=templates,
)

See examples/demo_cli/ for a complete demo application and its generated output in both formats.

API Reference

Types

  • Command -- Protocol that any CLI command class must satisfy (structural subtyping).
  • CommandClass -- Alias for type[Command].
  • CommandGroup -- NamedTuple grouping commands under a name.
  • ExampleInfo -- Dataclass for a usage example (info, usage).
  • ArgumentInfo -- Dataclass for a positional argument (name, usage, metavar, nargs, choices, default_value).
  • FlagInfo -- Dataclass for an optional argument (name, short, option_strings, usage, default_value, metavar, choices, required, is_flag).
  • TemplateInfo -- Dataclass for Jinja2 template configuration (index_file_name, index_template, command_template).

Functions

  • gen_docs(command_class, writer, template, appname, command_groups, command_config=None, *, extra_filters=None) -- Render docs for a single command to a text stream.
  • gen_docs_tree(appname, command_groups, output_dir, templates, file_prepender=None, file_extension=".md", command_config=None, *, file_prefix="", extra_filters=None, dry_run=False) -- Generate a full documentation tree (one file per non-hidden command, plus the index). Returns the command file names. Every page is rendered before anything is written, so a template error leaves no partial output.
  • validate_templates(templates, *, appname="app", extra_filters=None) -- Render both templates against synthetic data without touching the file system. Raises the same Jinja2 errors generation would.
  • get_bundled_templates(format="rst", index_file_name=None) -- Load the bundled reST or Markdown templates.

Parameters worth knowing:

Parameter Purpose
command_config Passed as the sole constructor argument to every command class (craft-cli passes its app config this way). Without it, gencodo tries cls(None) and then cls().
file_prefix Prefix for command file names, e.g. "myapp-" produces myapp-greet.rst.
file_prepender Callable[[str], str] whose result is written at the top of each file; receives the file name. Use it for banners or front matter.
extra_filters Mapping of extra Jinja2 filters. Names that clash with built-ins override them. Pass the same mapping to validate_templates.
dry_run Render everything, write nothing. Combine with your real command groups to validate templates in CI.

Command Protocol

Your command classes need these attributes/methods:

Attribute Type Description
name str Command name
help_msg str Short help string
hidden bool Exclude from docs if True
fill_parser(parser) method Add arguments to an ArgumentParser

Optional attributes, read with getattr:

Attribute Type Description
overview str Longer description (rendered as long)
examples list[tuple[str, str]] (description, command) pairs
related_commands list[str] | None Explicit related commands, or None to list the non-hidden siblings in the same group
common bool craft-cli's "common command" marker, exposed as common

Templates

Templates are Jinja2 with trim_blocks, lstrip_blocks, keep_trailing_newline, and StrictUndefined: a typo in a variable name fails generation rather than rendering an empty string.

Bundled templates

get_bundled_templates("rst") and get_bundled_templates("md") return the templates under src/gencodo/templates/. They render usage, overview, arguments, options (with short forms, choices, required markers, and defaults), examples, and related commands; the reST index groups commands in a toctree and a quick-reference table, the Markdown index uses lists and a table.

Custom templates

Pass your own template strings via TemplateInfo:

from gencodo import TemplateInfo, validate_templates

templates = TemplateInfo(
    index_file_name="index.md",
    index_template="# Commands\n{% for f in files %}- [{{ f.command_name }}]({{ f.filename }})\n{% endfor %}",
    command_template="# {{ command_name }}\n\n{{ short }}\n",
)
validate_templates(templates)  # fails fast on unknown variables or filters

Command template variables

Variable Type Description
command_name str Command name
ref str Anchor-friendly name (dashes and spaces to underscores)
filename str This command's output file name (with file_prefix and extension)
short str Short help message
long str Overview text, stripped
synopsis str Single-line usage, e.g. myapp greet [-f] name
heading_len int Length of command_name, for underlines
arguments list[ArgumentInfo] Positional arguments in declaration order
flags list[FlagInfo] Optional arguments in declaration order (suppressed ones excluded)
examples list[ExampleInfo] Usage examples
related_commands list[str] Related command names
appname str Application name
group_name str Name of the command's group
hidden, common bool The command's flags of the same name

Index template variables

Variable Type Description
appname str Application name
files list[dict] One entry per generated page: filename, command_name, short, group_name, ref
commands list[dict] Alias of files

Group with {% for group_name, entries in files | groupby('group_name') %}.

Filters

In addition to Jinja2's built-ins (replace, join, lower, upper, trim, groupby, ...):

Filter Example Result
indent(width, first=True, blank=False) {{ usage | indent(3) }} Indents every line (first line included by default; first=False when the template already indents it)
repeat(n) {{ '=' | repeat(heading_len) }} Repeats a string
slug {{ 'My App sub!' | slug }} my-app-sub
anchor {{ 'ref_my app' | anchor }} ref_my-app (keeps underscores)
title_case {{ 'list all' | title_case }} List All
trim_prefix(p) / trim_suffix(p) {{ short | trim_suffix('.') }}. Exactly one trailing period
replace_spaces(r='_') {{ command_name | replace_spaces }} my_command

Add your own with extra_filters.

Link patterns

  • reST: label each page with .. _ref_{{ ref }}: and link with :ref:`{{ cmd }} <ref_{{ cmd | replace('-', '_') }}>` ; the index can .. include:: or toctree the filename entries.
  • Markdown: link siblings by file name, [{{ cmd }}]({{ file_prefix }}{{ cmd }}.md), or by heading anchor with slug.

Behaviour notes

  • Hidden commands are skipped by gen_docs_tree and by related-command inference.
  • Options with help=argparse.SUPPRESS are omitted; explicit related_commands names are validated against all groups and raise ValueError if unknown.
  • synopsis comes from argparse's usage formatter with an unlimited width, so it is one line.
  • default_value is empty for None, argparse.SUPPRESS, and value-less actions (store_true, store_false, count); check is_flag to decide whether to show a default at all.
  • Command classes are instantiated once per page to fill a throwaway parser; craft-application's parse callbacks run as part of fill_parser.

Development

make install        # uv sync --group dev
make                # ruff, mypy, pytest
make test-coverage  # coverage report (htmlcov/)
make reuse          # REUSE license/copyright compliance
make examples       # regenerate examples/demo_cli/docs_output (CI checks it is current)

CI runs the tests on Python 3.10 to 3.14, ruff, mypy, REUSE, a build, and the example-output check.

Release process (maintainers)

  1. Set __version__ in src/gencodo/__init__.py (the only place the version lives) and move the [Unreleased] entries in CHANGELOG.md into a ## [X.Y.Z] - YYYY-MM-DD section; update the compare links.
  2. Commit, then run make release VERSION=vX.Y.Z. It checks the version and CHANGELOG, runs the checks, tags, and pushes the tag.
  3. The Publish to PyPI workflow builds, publishes through PyPI trusted publishing (environment pypi), and creates the GitHub release with the CHANGELOG section as notes; check gh release view vX.Y.Z and pip index versions gencodo-py.

License and copyright

gencodo-py is licensed under the LGPL-3.0-only and follows the REUSE specification: every file carries SPDX license and copyright information, either in a header or through REUSE.toml for files that cannot hold a comment. make reuse and the CI reuse job verify this.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gencodo_py-0.4.0.tar.gz (102.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gencodo_py-0.4.0-py3-none-any.whl (33.4 kB view details)

Uploaded Python 3

File details

Details for the file gencodo_py-0.4.0.tar.gz.

File metadata

  • Download URL: gencodo_py-0.4.0.tar.gz
  • Upload date:
  • Size: 102.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gencodo_py-0.4.0.tar.gz
Algorithm Hash digest
SHA256 f22937eab2b15bb34731cf9534a5d2509a01c054f3a1f5bf69e5a53d46cbe979
MD5 62fd9666944514710facd170b03874a7
BLAKE2b-256 bfea282264879c0003211ddb4c431f8f7a54f91a6080d470031e79241a54445f

See more details on using hashes here.

Provenance

The following attestation bundles were made for gencodo_py-0.4.0.tar.gz:

Publisher: publish.yml on canonical/gencodo-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gencodo_py-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: gencodo_py-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 33.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gencodo_py-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7d0a8e37113dc5c94a014618dc25c5613ba03bf8b338a69ef79d963ba1241f75
MD5 01d8344d97a44be582e53a7427f72dbc
BLAKE2b-256 525f7a2503310d1aa7e7ca8d3b9ce93b5dc5ab72b14730a7a6363ac973190693

See more details on using hashes here.

Provenance

The following attestation bundles were made for gencodo_py-0.4.0-py3-none-any.whl:

Publisher: publish.yml on canonical/gencodo-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

0.3.0

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page