web-dev-qa-db-fra.com

Comment implémenter l'option --verbose ou -v dans un script?

Je connais le --verbose ou le -v de plusieurs outils et j'aimerais intégrer cela dans certains de mes propres scripts et outils.

J'ai pensé à placer:

if verbose:
    print ...

via mon code source, afin que si un utilisateur passe l'option -v, la variable verbose sera définie sur True et le texte sera imprimé.

Est-ce la bonne approche ou existe-t-il un moyen plus courant?

Ajout: je ne demande pas un moyen d'implémenter l'analyse des arguments. Je sais comment c'est fait. Je ne m'intéresse que spécialement à l'option verbeuse.

69
Aufwind

Ma suggestion est d'utiliser une fonction. Mais plutôt que de mettre la if dans la fonction, ce que vous pourriez être tenté de faire, procédez comme suit:

if verbose:
    def verboseprint(*args):
        # Print each argument separately so caller doesn't need to
        # stuff everything to be printed into a single string
        for arg in args:
           print arg,
        print
else:   
    verboseprint = lambda *a: None      # do-nothing function

(Oui, vous pouvez définir une fonction dans une instruction if. Elle ne sera définie que si la condition est vraie!)

Si vous utilisez Python 3, où print est déjà une fonction (ou si vous êtes prêt à utiliser print comme fonction dans 2.x à l'aide de from __future__ import print_function), la procédure est encore plus simple:

verboseprint = print if verbose else lambda *a, **k: None

De cette façon, la fonction est définie comme une opération à ne rien faire si le mode commenté est désactivé (utilisation d'un lambda), au lieu de tester en permanence l'indicateur verbose

Si l'utilisateur pouvait changer le mode de verbosité pendant l'exécution de votre programme, ce serait une mauvaise approche (vous auriez besoin de la variable if dans la fonction), mais comme vous le définissez avec un indicateur de ligne de commande , il vous suffit de prendre la décision une fois.

Vous utilisez ensuite par exemple verboseprint("look at all my verbosity!", object(), 3) chaque fois que vous souhaitez imprimer un message "détaillé".

87
kindall

Utilisez le module logging:

import logging as log
…
args = p.parse_args()
if args.verbose:
    log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
    log.info("Verbose output.")
else:
    log.basicConfig(format="%(levelname)s: %(message)s")

log.info("This should be verbose.")
log.warning("This is a warning.")
log.error("This is an error.")

Tous ces éléments passent automatiquement à stderr:

% python myprogram.py
WARNING: This is a warning.
ERROR: This is an error.

% python myprogram.py -v
INFO: Verbose output.
INFO: This should be verbose.
WARNING: This is a warning.
ERROR: This is an error.

Pour plus d'informations, consultez le Python Docs et les tutoriels .

49
Profpatsch

Construire et simplifier la réponse de @ kindall, voici ce que j'utilise généralement:

v_print = None
def main()
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbosity', action="count", 
                        help="increase output verbosity (e.g., -vv is more than -v)")

    args = parser.parse_args()

    if args.verbosity:
        def _v_print(*verb_args):
            if verb_args[0] > (3 - args.verbosity):
                print verb_args[1]  
    else:
        _v_print = lambda *a: None  # do-nothing function

    global v_print
    v_print = _v_print

if __== '__main__':
    main()

Ceci fournit ensuite l'utilisation suivante dans tout votre script:

v_print(1, "INFO message")
v_print(2, "WARN message")
v_print(3, "ERROR message")

Et votre script peut s'appeler comme ceci:

% python verbose-tester.py -v
ERROR message

% python verbose=tester.py -vv
WARN message
ERROR message

% python verbose-tester.py -vvv
INFO message
WARN message
ERROR message

Quelques notes:

  1. Votre premier argument est votre niveau d'erreur, et le second est votre message. Il a le nombre magique de 3 qui définit la limite supérieure de votre journalisation, mais j’accepte cela comme compromis pour la simplicité.
  2. Si vous voulez que v_print fonctionne tout au long de votre programme, vous devez faire le bric-à-brac avec le global. Ce n'est pas amusant, mais je défie quelqu'un de trouver un meilleur moyen.
9
mlissner

Ce que je fais dans mes scripts est de vérifier au moment de l'exécution si l'option 'verbose' est définie, puis de définir mon niveau de journalisation sur débogage. Si ce n'est pas défini, je le règle sur info. De cette façon, vous n'avez pas de contrôle 'si verbose' sur votre code. 

9
jonesy

J'ai volé le code de journalisation de virtualenv pour un de mes projets. Regardez dans main() of virtualenv.py pour voir comment il est initialisé. Le code est saupoudré de logger.notify(), logger.info(), logger.warn(), etc. Les méthodes qui émettent réellement la sortie sont déterminées par le fait que virtualenv a été appelé avec -v, -vv, -vvv ou -q.

2
George V. Reilly

Cela pourrait être plus propre si vous avez une fonction, appelée par exemple vprint, qui vérifie l'indicateur prolixe pour vous. Ensuite, vous appelez simplement votre propre fonction vprint à tout endroit où vous souhaitez une verbosité facultative.

2
Lee-Man

Il pourrait y avoir une variable globale, probablement définie avec argparse à partir de sys.argv, indiquant si le programme doit être détaillé ou non . Ensuite, un décorateur pourrait être écrit de telle sorte que si la verbosité était activée, l’entrée standard serait alors déviée. le périphérique nul tant que la fonction devait s'exécuter:

import os
from contextlib import redirect_stdout
verbose = False

def louder(f):
    def loud_f(*args, **kwargs):
        if not verbose:
            with open(os.devnull, 'w') as void:
                with redirect_stdout(void):
                    return f(*args, **kwargs)
        return f(*args, **kwargs)
    return loud_f

@louder
def foo(s):
    print(s*3)

foo("bar")

Cette réponse est inspirée par ce code ; En fait, j'allais l'utiliser comme module dans mon programme, mais j'avais des erreurs que je ne comprenais pas, alors j'ai adapté une partie de celle-ci.

L'inconvénient de cette solution est que la verbosité est binaire, contrairement à logging, qui permet d'ajuster avec précision la manière dont le programme peut être verbeux De plus, les appels tousprint sont déviés, ce qui peut être indésirable.

1
Daniel Diniz

La solution de @ kindall ne fonctionne pas avec ma version 3.5 de Python. @styles indique correctement dans son commentaire que la raison est l'argument optionnel supplémentaire keywords Ainsi, ma version légèrement raffinée pour Python 3 ressemble à ceci:

if VERBOSE:
    def verboseprint(*args, **kwargs):
        print(*args, **kwargs)
else:
    verboseprint = lambda *a, **k: None # do-nothing function
0
stefanct

Ce dont j'ai besoin est une fonction qui imprime un objet (obj), mais seulement si la variable globale verbose est vraie, sinon elle ne fait rien.

Je veux pouvoir changer le paramètre global "verbose" à tout moment. La simplicité et la lisibilité sont pour moi d'une importance primordiale. Je procéderais donc comme l'indiquent les lignes suivantes:

ak@HP2000:~$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> verbose = True
>>> def vprint(obj):
...     if verbose:
...         print(obj)
...     return
... 
>>> vprint('Norm and I')
Norm and I
>>> verbose = False
>>> vprint('I and Norm')
>>> 

La variable globale "verbose" peut également être définie à partir de la liste de paramètres.

0
user377367