pythonloggingargparseglobaldebug-mode

How to pass global debug flag variable throughout my code; should I use argparse?


Let's say I have a main program (test.py) and a little utilities program (test_utils.py) that has helper functions called by the main program. I would like to turn on debug statements in the code by passing a debug_flag boolean, which is read in via argparse.

Now I would like functions within my test_utils.py program to print debug statements as well, according to the value of debug_flag. I could always add debug_flag as a parameter to each function definition in test_utils.py and pass the parameter when the function is called, but is there a better way here, like make debug_flag a global variable? But if I do declare debug_flag to be global from test.py, how would that be imported into test_utils.py?

What would be the most elegant/Pythonic approach here?

test.py:

import argparse
from test_utils import summation

def main():
    args = get_args()
    debug_flag = True if args[debug] == 'True' else False
    print summation(5, 6, 7)

def get_args():
    parser = argparse.ArgumentParser(description='Test program')
    parser.add_argument('-d','--debug', help='Debug True/False', default=False)
    args = vars(parser.parse_args())
    return args

test_utils.py:

from test import debug_flag

def summation(x, y, z):
    if debug_flag:
        print 'I am going to add %s %s and %s' % (x, y, z)
    return x + y + z

EDIT1: To clarify - if I pass in the debug flag through argparse and thereby do set debug_flag to True - how would this be propagated to functions within test_utils.py?

EDIT2: Based on the suggestion by @joran-beasley, here's what I've got.

test.py:

import argparse
import logging
from test_utils import summation

def main():
    args = get_args()
    logging.getLogger("my_logger").setLevel(logging.DEBUG if args['debug'] == 'True' else logging.WARNING)
    print summation(5, 6, 7)

def get_args():
    parser = argparse.ArgumentParser(description='Test program')
    parser.add_argument('-d','--debug', help='Debug True/False', required=True)
    args = vars(parser.parse_args())
    return args

main()

test_utils.py

import logging

log = logging.getLogger('my_logger')

def summation(x, y, z):
    log.debug('I am going to add %s %s and %s' % (x, y, z))
    return x + y + z

When I run test.py, I get:

$ python test.py -d True
No handlers could be found for logger "my_logger"
18

Solution

  • use logging

    # this logger will always now be logging.DEBUG level
    logging.getLogger("my_logger").setLevel(logging.DEBUG if args.debug else logging.WARNING)
    

    then use

    log = logging.getLogger("my_logger")
    ...
    log.warn("some text that should always be seen!")
    log.debug("some debug info!")
    

    you can then do things where you have multiple levels of logging

    log_level = logging.WARNING
    if args.verbose > 0:
       log_level = logging.INFO
    elif args.verbose > 3:
       log_level = logging.DEBUG
    

    if for some reason you needed to retrieve the currentEffectiveLogLevel (you really shouldnt in most cases ... simply use log.debug when you want debug level output)

    logging.getLogger("my_logger").getEffectiveLevel()
    

    [edit to clarify]

    log = logging.getLogger('my_logger')
    
    def summation(x, y, z):
       log.debug('I am going to add %s %s and %s' % (x, y, z)) # will not print if my_logger does not have an effective level of debug
       return x + y + z
    
    print(summation(2, 3, 4))
    log.setLevel(logging.DEBUG)
    print(summation(4, 5, 6))
    

    or you can wrie a helper funtion

    def is_debug():
        return logging.getLogger("my_logger").getEffectiveLevel() == logging.DEBUG
    

    of coarse you can always do some horrible hacky crap also like writing it to a flat file and reading it in or using truly global variables (harder than you might think and lots of edge cases to worry about)