[PYTHON] Ergänzen Sie argparse aus docstrings

Einführung

Wenn Sie ein Befehlszeilenprogramm mit Python erstellen, ist click als Argumentparser praktisch. Wenn ich jedoch versuche, komplizierte Dinge zu tun, finde ich, dass argparse einfacher zu verwenden ist. Wenn Sie dagegen argparse verwenden, müssen Sie Unterbefehlen und Argumenten eine Beschreibung und Hilfe geben. In den meisten Fällen werden die Informationen mit der Dokumentzeichenfolge dupliziert, was zweimal problematisch ist. Überlegen wir uns also, wie Sie Informationen mit docstring an argparse übergeben können.

Es wird davon ausgegangen, dass Dokumentzeichenfolgen gemäß Google Style Guide geschrieben wurden.

Programmweite Beschreibung

Beschreibung des Befehlshauptteils, dh des Beschreibungsarguments von argparse.ArgumentParser. Dies sollte sich in der Dokumentzeichenfolge des Moduls befinden, das die Hauptfunktion hat. Daher ist die Erstellung von ArgumentParser

parser = argparse.ArgumentParser(
  description=__doc__, formatter_class=argparse.RawTextHelpFormatter)

Wenn ja, sieht es gut aus. Beachten Sie, dass argparse.RawTextHelpFormatter an formatter_class übergeben wird. Dies soll verhindern, dass Zeilenumbrüche und Leerzeichen aus der Beschreibung und der danach festgelegten Hilfe gelöscht werden.

Unterbefehlsbeschreibung

Es wird angenommen, dass der Unterbefehl mit der Funktion gebündelt ist, die ihn ausführt. Zumindest

a1_cmd = subparsers.add_parser("a1")
a1_cmd.set_defaults(cmd=a1)

Ich glaube, es ist. Wenn ja, gegeben eine Funktion, die den Unterbefehl implementiert, hier a1,

a1_cmd = subparsers.add_parser(
  a1.__name__, help=a1.__doc__, description=a1.__doc__)
a1_cmd.set_defaults(cmd=a1)

Sie sollten in der Lage sein, so etwas zu tun. Wenn jedoch nichts unternommen wird, wird in Hilfe und Beschreibung ein langer Satz mit Argumentinformationen gesetzt. Die Überschriftenfunktion, die nur die erste Zeile der Dokumentzeichenfolge erhält, Bis die Erklärung des Arguments eingegeben wird (im Google-Stil, bis Args: Returns: Raises: Yields: erscheint) Bereiten Sie eine Beschreibungsfunktion vor, um sie abzurufen

a1_cmd = subparsers.add_parser(
  a1.__name__,
  help=headline(a1.__doc__),
  description=description(a1.__doc__))
a1_cmd.set_defaults(cmd=a1)

Sagen wir. (Ich glaube nicht, dass es Erträge gibt, aber vorerst) Die Überschriftenfunktion und die Beschreibungsfunktion sehen wie folgt aus.

def headline(text):
    """ Returns a head line of a given text.
    """
    return text.split("\n")[0]

keywords =("Args:", "Returns:", "Raises:", "Yields:")
def checker(v):
    """ Check a given value not starts with keywords.
    """
    for k in keywords:
        if k in v:
            return False
    return True

def description(text):
    """ Returns a text before `Args:`, `Returns:`, `Raises:`, or `Yields:`.
    """
    lines = list(itertools.takewhile(checker, text.split("\n")))
    if len(lines) < 2:
        return lines[0]
    return "{0}\n\n{1}".format(lines[0], textwrap.dedent("\n".join(lines[2:])))

Hilfe für jedes Argument

Das Hilfeargument, wenn "a1_cmd.add_argument (" name ", help =" Beschreibung dieses Arguments ") verwendet wird. Diese sind wahrscheinlich in der Dokumentzeichenfolge der Funktion geschrieben, die dem Unterbefehl entspricht. Im Google-Stil sollte es in einer Form geschrieben werden, die dem Wörterbuchformat nach "Args:" nahe kommt. Rufen Sie daher die Erklärungsspalte des Arguments von docstring ab. Sie können es als Hilfeargument übergeben, wenn das entsprechende Argument mit add_argument hinzugefügt wird.

Zusätzlich zu der Überschrift und Beschreibung oben müssen wir die Dokumentzeichenfolge genauer analysieren. Funktionen gemeinsam vorbereiten.

_KEYWORDS_ARGS = ("Args:",)
_KEYWORDS_OTHERS = ("Returns:", "Raises:", "Yields:")
_KEYWORDS = _KEYWORDS_ARGS + _KEYWORDS_OTHERS

def checker(keywords):
    """Generate a checker which tests a given value not starts with keywords."""
    def _(v):
        """Check a given value matches to keywords."""
        for k in keywords:
            if k in v:
                return False
        return True
    return _

def parse_doc(doc):
    """Parse a docstring.
    Parse a docstring and extract three components; headline, description,
    and map of arguments to help texts.
    Args:
      doc: docstring.
    Returns:
      a dictionary.
    """
    lines = doc.split("\n")
    descriptions = list(itertools.takewhile(checker(_KEYWORDS), lines))

    if len(descriptions) < 3:
        description = lines[0]
    else:
        description = "{0}\n\n{1}".format(
            lines[0], textwrap.dedent("\n".join(descriptions[2:])))

    args = list(itertools.takewhile(
        _checker(_KEYWORDS_OTHERS),
        itertools.dropwhile(_checker(_KEYWORDS_ARGS), lines)))
    argmap = {}
    if len(args) > 1:
        for pair in args[1:]:
            kv = [v.strip() for v in pair.split(":")]
            if len(kv) >= 2:
                argmap[kv[0]] = kv[1]

    return dict(headline=descriptions[0], description=description, args=argmap)

Wenn Sie eine Dokumentzeichenfolge an dieses parse_doc übergeben, werden eine Überschrift, eine Beschreibung und ein Args-Wörterbuch zurückgegeben. Die beiden ersteren sind

a1_doc = parse_doc(a1)
a1_cmd = subparsers.add_parser(
  a1.__name__,
  help=a1_doc["headline"],
  description=a1_doc["description"])
a1_cmd.set_defaults(cmd=a1)

Das Argumentwörterbuch kann wie folgt verwendet werden

a1_cmd.add_argument("name", help=a1_doc["args"]["name"])

Es kann wie verwendet werden.

Zusammenfassung und Bibliothek

Auf die oben beschriebene Weise können Sie aus Dokumentzeichenfolgen vervollständigen, ohne jedes Mal eine Beschreibung oder Hilfe zu schreiben. Es ist jedoch mühsam, diese jedes Mal vorzubereiten, wenn Sie eine Befehlszeilenanwendung schreiben Die Bibliothek dsargparse wird vorbereitet. Da die oben genannten Inhalte als Wrapper von Argparse realisiert werden, ist es nicht erforderlich, fast alles von der Benutzerseite aus zu schreiben. Siehe Beispiel.

Recommended Posts

Ergänzen Sie argparse aus docstrings
Portierung von Argparse zu Hydra
Argparse Teil 1
argparse note
Python Argparse Vorlage
Ich habe versucht, Argparse zu verwenden
Ergänzen Sie argparse aus docstrings
Docstrings