From 46fc6505d94184fbc7841c451d09107a74bb5fc3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Morin Date: Fri, 17 Nov 2023 22:26:40 -0500 Subject: [PATCH] Add command reference in docs Signed-off-by: Jean-Christophe Morin --- docs/source/command.rst | 5 ++ docs/source/conf.py | 146 +++++++++++++++++++++++++++++++++++++++- docs/source/index.rst | 1 + src/rez_pip/cli.py | 14 ++-- 4 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 docs/source/command.rst diff --git a/docs/source/command.rst b/docs/source/command.rst new file mode 100644 index 0000000..ce85bf3 --- /dev/null +++ b/docs/source/command.rst @@ -0,0 +1,5 @@ +======= +Command +======= + +.. rez-autoargparse:: diff --git a/docs/source/conf.py b/docs/source/conf.py index 201d561..ce37115 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,10 +3,16 @@ # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html +import argparse +import re + from sphinx.application import Sphinx from sphinx.transforms import SphinxTransform from docutils.nodes import reference, Text -import re +import sphinx.util.docutils +import docutils.nodes + +import rez_pip.cli # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information @@ -93,5 +99,143 @@ def apply(self): return +class RezAutoArgparseDirective(sphinx.util.docutils.SphinxDirective): + """ + Special rez-autoargparse directive. This is quite similar to "autosummary" in some ways. + """ + + required_arguments = 0 + optional_arguments = 0 + + def run(self) -> list[docutils.nodes.Node]: + # Create the node. + node = docutils.nodes.section() + node.document = self.state.document + + rst = docutils.statemachine.ViewList() + + # Add rezconfig as a dependency to the current document. The document + # will be rebuilt if rezconfig changes. + self.env.note_dependency(rez_pip.cli.__file__) + self.env.note_dependency(__file__) + + path, lineNumber = self.get_source_info() + + parser = rez_pip.cli._createParser() + + full_cmd = parser.prog.replace(" ", "-") + + # Title + document = [f".. _{full_cmd}:"] + document.append("") + document.append(f"{'='*len(parser.prog)}") + document.append(f"{full_cmd}") + document.append(f"{'='*len(parser.prog)}") + document.append("") + + document.append(f".. program:: {full_cmd}") + document.append("") + document.append("Usage") + document.append("=====") + document.append("") + document.append(".. code-block:: text") + document.append("") + for line in parser.format_usage()[7:].split("\n"): + document.append(f" {line}") + document.append("") + + document.append("Description") + document.append("===========") + document.extend(parser.description.split("\n")) + + for group in parser._action_groups: + if not group._group_actions: + continue + + document.append("") + title = group.title.capitalize() + document.append(title) + document.append("=" * len(title)) + document.append("") + + for action in group._group_actions: + if isinstance(action, argparse._HelpAction): + continue + + # Quote default values for string/None types + default = action.default + if ( + action.default not in ["", None, True, False] + and action.type in [None, str] + and isinstance(action.default, str) + ): + default = f'"{default}"' + + # fill in any formatters, like %(default)s + format_dict = dict(vars(action), prog=parser.prog, default=default) + format_dict["default"] = default + help_str = action.help or "" # Ensure we don't print None + try: + help_str = help_str % format_dict + except Exception: + pass + + if help_str == argparse.SUPPRESS: + continue + + # Avoid Sphinx warnings. + help_str = help_str.replace("*", "\\*") + # Replace everything that looks like an argument with an option directive. + help_str = re.sub( + r"(?`", + help_str, + ) + help_str = help_str.replace("--", "\\--") + + # Add links to rez docs for known settings. + help_str = re.sub( + "(.* \(default: configured )([a-zA-Z_]+)(.*)$", + r"\g<1> :external:data:`\g<2>`\g<3>", + help_str, + ) + + # Options have the option_strings set, positional arguments don't + name = action.option_strings + if name == []: + if action.metavar is None: + name = [action.dest] + else: + name = [action.metavar] + + # Skip lines for subcommands + if name == [argparse.SUPPRESS]: + continue + + metavar = action.metavar if action.metavar else "" + document.append(f".. option:: {', '.join(name)} {metavar.lower()}") + document.append("") + document.append(f" {help_str}") + if action.choices: + document.append("") + document.append(f" Choices: {', '.join(action.choices)}") + document.append("") + + document = "\n".join(document) + + # Add each line to the view list. + for index, line in enumerate(document.split("\n")): + # Note to future people that will look at this. + # "line" has to be a single line! It can't be a line like "this\nthat". + rst.append(line, path, lineNumber + index) + + # Finally, convert the rst into the appropriate docutils/sphinx nodes. + sphinx.util.nodes.nested_parse_with_titles(self.state, rst, node) + + # Return the generated nodes. + return node.children + + def setup(app: Sphinx): + app.add_directive("rez-autoargparse", RezAutoArgparseDirective) app.add_transform(ReplaceGHRefs) diff --git a/docs/source/index.rst b/docs/source/index.rst index eb7848c..3cd4830 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -66,6 +66,7 @@ automatically created by the `install.py typing.List[str]: return __all__ -def _parseArgs( - args: typing.List[str], -) -> typing.Tuple[argparse.Namespace, typing.List[str]]: +def _createParser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description="Ingest and convert python packages to rez packages.", prog=__package__.replace("_", "-"), @@ -72,7 +70,7 @@ def _parseArgs( generalGroup.add_argument( "--release", action="store_true", - help="Release the converted packages (Default: configured release_packages_path)", + help="Release the converted packages (default: configured release_packages_path)", ) generalGroup.add_argument( @@ -120,6 +118,14 @@ def _parseArgs( %(prog)s [options] %(prog)s [-- [pip options]] """ + return parser + + +def _parseArgs( + args: typing.List[str], +) -> typing.Tuple[argparse.Namespace, typing.List[str]]: + parser = _createParser() + knownArgs = [] pipArgs = [] if "--" in args: