Reputation: 26278
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.
Upvotes: 136
Views: 208765
Reputation: 184345
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.
If you are willing and able to use the Python -O
flag to turn verbosity on and off when launching the "production" version of the script (or set the PYTHONOPTIMIZE
environment variable) then the better way is to test the __debug__
flag everywhere you want to print a verbose output:
if __debug__: print("Verbosity enabled")
When run without optimization, these statements are executed (and the if
is stripped out, leaving only the body of the statement). When run with optimization, Python actually strips those statements out entirely. They have no performance impact whatsoever! See my blog post on __debug__
and -O
for a more in-depth discussion.
Upvotes: 142
Reputation: 174
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.
Upvotes: 2
Reputation: 2964
@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
Upvotes: 5
Reputation: 16333
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
.
Upvotes: 5
Reputation: 119
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.
Upvotes: 1
Reputation: 5058
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.
Upvotes: 81
Reputation: 18206
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.Upvotes: 20
Reputation: 3542
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.
Upvotes: 9
Reputation: 414
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.
Upvotes: 6