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.
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.
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.
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.
@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
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.
I stole the logging code from virtualenv for a project of mine. Look in main()
of virtualenv.py
to see how it's initialized. 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
.
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:
3
that sets the upper bound for your logging, but I accept that as a compromise for simplicity.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.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.
Source: Stackoverflow.com