How to implement the --verbose or -v option into a script?

I know the --verbose or -v from several tools and I'd like to implement this into some of my own scripts and tools.

I thought of placing:

if verbose:
print ...

through my source code, so that if a user passes the -v option, the variable verbose will be set to True and the text will be printed.

Is this the right approach or is there a more common way?

Addition: I am not asking for a way to implement the parsing of arguments. That I know how it is done. I am only interested specially in the verbose option.

163663 次浏览

It might be cleaner if you have a function, say called vprint, that checks the verbose flag for you. Then you just call your own vprint function any place you want optional verbosity.

My suggestion is to use a function. But rather than putting the if in the function, which you might be tempted to do, do it like this:

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

(Yes, you can define a function in an if statement, and it'll only get defined if the condition is true!)

If you're using Python 3, where print is already a function (or if you're willing to use print as a function in 2.x using from __future__ import print_function) it's even simpler:

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

This way, the function is defined as a do-nothing if verbose mode is off (using a lambda), instead of constantly testing the verbose flag.

If the user could change the verbosity mode during the run of your program, this would be the wrong approach (you'd need the if in the function), but since you're setting it with a command-line flag, you only need to make the decision once.

You then use e.g. verboseprint("look at all my verbosity!", object(), 3) whenever you want to print a "verbose" message.

What I do in my scripts is check at runtime if the 'verbose' option is set, and then set my logging level to debug. If it's not set, I set it to info. This way you don't have 'if verbose' checks all over your code.

Building and simplifying @kindall's answer, here's what I typically use:

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 __name__ == '__main__':
main()

This then provides the following usage throughout your script:

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

And your script can be called like this:

% 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

A couple notes:

  1. Your first argument is your error level, and the second is your message. It has the magic number of 3 that sets the upper bound for your logging, but I accept that as a compromise for simplicity.
  2. If you want v_print to work throughout your program, you have to do the junk with the global. It's no fun, but I challenge somebody to find a better way.

Use the logging module:

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.")

All of these automatically go to 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.

For more info, see the Python Docs and the tutorials.

I stole the logging code from virtualenv.py0 for a project of mine. Look in main() of virtualenv.py to see virtualenv.py1 The code is sprinkled with logger.notify(), logger.info(), logger.warn(), and the like. Which methods actually emit output is determined by whether virtualenv was invoked with -v, -vv, -vvv, or -q.

What I need is a function which prints an object (obj), but only if global variable verbose is true, else it does nothing.

I want to be able to change the global parameter "verbose" at any time. Simplicity and readability to me are of paramount importance. So I would proceed as the following lines indicate:

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

Global variable "verbose" can be set from the parameter list, too.

@kindall's solution does not work with my Python version 3.5. @styles correctly states in his comment that the reason is the additional optional keywords argument. Hence my slightly refined version for Python 3 looks like this:

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

There could be a global variable, likely set with argparse from sys.argv, that stands for whether the program should be verbose or not. Then a decorator could be written such that if verbosity was on, then the standard input would be diverted into the null device as long as the function were to run:

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

This answer is inspired by this code; actually, I was going to just use it as a module in my program, but I got errors I couldn't understand, so I adapted a portion of it.

The downside of this solution is that verbosity is binary, unlike with logging, which allows for finer-tuning of how verbose the program can be. Also, all print calls are diverted, which might be unwanted for.