Rework the docs script to work with click

This commit is contained in:
Ivan Habunek 2024-03-26 10:04:32 +01:00
parent b0c21ac436
commit 43a4a6c4f5
No known key found for this signature in database
GPG Key ID: F5F0623FF5EBCB3D

View File

@ -4,13 +4,16 @@
Auto-generates documentation from command defs in console.py. Auto-generates documentation from command defs in console.py.
""" """
import click
import html import html
import os import os
import re import re
import shutil import shutil
import textwrap import textwrap
from twitchdl.console import COMMANDS from click import Command
from twitchdl.cli import cli
START_MARKER = "<!-- ------------------- generated docs start ------------------- -->" START_MARKER = "<!-- ------------------- generated docs start ------------------- -->"
@ -19,8 +22,11 @@ END_MARKER = "<!-- ------------------- generated docs end ------------------- --
def main(): def main():
update_changelog() update_changelog()
for command in COMMANDS:
update_docs(command) parent_ctx = click.Context(cli, info_name="twitch-dl")
for name, command in cli.commands.items():
ctx = click.Context(cli, info_name=name, parent=parent_ctx)
update_docs(command, ctx)
def update_changelog(): def update_changelog():
@ -31,9 +37,9 @@ def update_changelog():
shutil.copy(source, target) shutil.copy(source, target)
def update_docs(command): def update_docs(command: Command, ctx: click.Context):
path = os.path.join("docs", "commands", f"{command.name}.md") path = os.path.join("docs", "commands", f"{command.name}.md")
content = render_command(command) content = render_command(command, ctx)
if not os.path.exists(path): if not os.path.exists(path):
print(f"Creating: {path}") print(f"Creating: {path}")
@ -45,87 +51,29 @@ def update_docs(command):
write(path, content) write(path, content)
def render_command(command): def render_command(command: Command, ctx: click.Context):
content = START_MARKER content = START_MARKER
content += f"\n# twitch-dl {command.name}\n\n" content += f"\n# twitch-dl {command.name}\n\n"
content += command.description + "\n\n"
content += render_usage(command) if command.help:
content += render_arguments(command) content += command.help + "\n\n"
content += render_flags(command)
content += render_options(command) content += render_usage(ctx, command)
content += render_options(ctx, command)
return content return content
def render_usage(command): def render_usage(ctx: click.Context, command: Command):
arguments = get_arguments(command)
arguments = " ".join(f"<{name}>" for [name, _] in arguments)
flags = get_flags(command)
options = get_options(command)
content = "### USAGE\n\n" content = "### USAGE\n\n"
content += "```\n" content += "```\n"
content += f"twitch-dl {command.name} {arguments}" content += command.get_usage(ctx).replace("Usage: ", "")
if flags:
content += " [FLAGS]"
if options:
content += " [OPTIONS]"
content += "\n```\n\n" content += "\n```\n\n"
return content return content
def render_arguments(command): def render_options(ctx, command: Command):
arguments = get_arguments(command) options = list(get_options(command))
if not arguments:
return ""
content = "### ARGUMENTS\n\n"
content += "<table>\n"
content += "<tbody>"
for [name, params] in arguments:
content += textwrap.dedent(f"""
<tr>
<td class="code">&lt;{escape(name)}&gt;</td>
<td>{escape(params['help'])}</td>
</tr>
""")
content += "</tbody>\n"
content += "</table>\n\n"
return content
def render_flags(command):
flags = get_flags(command)
if not flags:
return ""
content = "### FLAGS\n\n"
content += "<table>\n"
content += "<tbody>"
for [names, params] in flags:
names = ", ".join(f"{name}" for name in names)
content += textwrap.dedent(f"""
<tr>
<td class="code">{escape(names)}</td>
<td>{escape(params['help'])}</td>
</tr>
""")
content += "</tbody>\n"
content += "</table>\n\n"
return content
def render_options(command):
options = get_options(command)
if not options: if not options:
return "" return ""
@ -134,12 +82,11 @@ def render_options(command):
content += "<table>\n" content += "<table>\n"
content += "<tbody>" content += "<tbody>"
for [names, params] in options: for opts, help in options:
names = ", ".join(f"{name}" for name in names)
content += textwrap.dedent(f""" content += textwrap.dedent(f"""
<tr> <tr>
<td class="code">{escape(names)}</td> <td class="code">{escape(opts)}</td>
<td>{escape(params['help'])}{choices(params)}</td> <td>{escape(help)}</td>
</tr> </tr>
""") """)
content += "</tbody>\n" content += "</tbody>\n"
@ -148,37 +95,39 @@ def render_options(command):
return content return content
def choices(params): def get_options(command: Command):
if "choices" in params: for option in command.params:
choices = ", ".join(code(c) for c in params["choices"]) if isinstance(option, click.Option):
opts = ", ".join(option.opts)
opts += option_type(option)
help = option.help or ""
help = re.sub(r"\s+", " ", help)
help += choices(option)
if option.default:
help += f" [default: `{option.default}`]"
yield opts, help
def option_type(option: click.Option):
match option.type:
case click.types.StringParamType():
return " TEXT"
case click.types.Choice():
return " TEXT"
case click.types.IntParamType():
return " INTEGER"
case _:
return ""
def choices(option: click.Option):
if isinstance(option.type, click.Choice):
choices = ", ".join(f"`{c}`" for c in option.type.choices)
return f" Possible values: {choices}." return f" Possible values: {choices}."
return "" return ""
def get_arguments(command):
return [
[names[0], options]
for names, options in command.arguments
if len(names) == 1 and not names[0].startswith("-")
]
def get_flags(command):
return [
[names, options]
for names, options in command.arguments
if names[0].startswith("-") and "type" not in options
]
def get_options(command):
return [
[names, options]
for names, options in command.arguments
if names[0].startswith("-") and "type" in options
]
def read(path): def read(path):
with open(path, "r") as f: with open(path, "r") as f:
return f.read() return f.read()
@ -189,10 +138,6 @@ def write(path, content):
return f.write(content) return f.write(content)
def code(string):
return f"<code>{string}</code>"
def escape(text: str): def escape(text: str):
text = html.escape(text) text = html.escape(text)
text = re.sub(r"`([\S]+)`", "<code>\\1</code>", text) text = re.sub(r"`([\S]+)`", "<code>\\1</code>", text)