[PYTHON] Compléter argparse de docstrings

introduction

Lors de la création d'un outil de ligne de commande avec python, le clic est pratique comme analyseur d'arguments. Cependant, en essayant de faire des choses compliquées, je pense que argparse est plus facile à utiliser. D'autre part, lors de l'utilisation d'argparse, il est nécessaire de donner une description et une aide aux sous-commandes et arguments. Dans la plupart des cas, les informations sont dupliquées avec la docstring, ce qui est gênant deux fois. Alors, réfléchissons à la façon de transmettre des informations à argparse à l'aide de docstring.

On suppose que les docstrings sont écrites conformément au Google Style Guide.

Description à l'échelle du programme

Description du corps de la commande, c'est-à-dire l'argument description de argparse.ArgumentParser. Cela devrait être dans la docstring du module qui a la fonction principale. Par conséquent, la création d'ArgumentParser est

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

Si oui, ça a l'air bien. Notez que argparse.RawTextHelpFormatter est passé à formatter_class. Cela permet d'éviter que les sauts de ligne et les blancs ne soient supprimés de la description et de l'aide définie par la suite.

Description de la sous-commande

On suppose que la sous-commande est fournie avec la fonction qui l'exécute. C'est, au moins

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

Je pense que c'est. Si tel est le cas, étant donné une fonction qui implémente la sous-commande, ici ʻa1`,

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

Vous devriez pouvoir faire quelque chose comme ça. Cependant, si rien n'est fait, une longue phrase contenant des informations sur les arguments sera définie dans l'aide et la description. La fonction de titre qui n'obtient que la première ligne de la docstring, Jusqu'à l'explication de l'argument (jusqu'à ce que ʻArgs: renvoie: Raises: `` Yields: ʻapparaît dans le style Google) Préparez une fonction de description pour obtenir

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

Disons. (Je ne pense pas qu'il y ait des rendements, mais pour le moment) La fonction de titre et la fonction de description ressemblent à ce qui suit.

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:])))

Aide pour chaque argument

ʻA1_cmd.add_argument (“name”, help = “description de cet argument.”) ʻEtc. Help argument. Ceux-ci sont probablement écrits dans la docstring de la fonction correspondant à la sous-commande. Dans le style Google, il doit être écrit sous une forme proche du format du dictionnaire après ʻArgs: `. Par conséquent, récupérez la colonne d'explication de l'argument de docstring. Vous pouvez le passer comme argument d'aide lorsque l'argument correspondant est ajouté avec add_argument.

En plus du titre et de la description ci-dessus, nous devons analyser la docstring plus en détail. Préparez les fonctions collectivement.

_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)

Passer une docstring à ce parse_doc renverra un titre, une description et un dictionnaire args. Les deux premiers sont

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)

Le dictionnaire d'arguments peut être utilisé comme

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

Il peut être utilisé comme.

Résumé et bibliothèque

En faisant ce qui précède, vous pouvez compléter à partir de docstrings sans écrire de description ou d'aide à chaque fois. Cependant, il est difficile de les préparer chaque fois que vous écrivez une application de ligne de commande, donc La bibliothèque dsargparse est préparée. Puisque le contenu ci-dessus est réalisé comme un wrapper d'argparse, il n'est pas nécessaire d'écrire presque quoi que ce soit du côté utilisateur. Voir l'exemple.

Recommended Posts

Compléter argparse de docstrings
Portage d'Argparse à Hydra
argparse partie 1
argparse note
modèle argparse python
J'ai essayé d'utiliser argparse
Compléter argparse de docstrings
Docstrings