Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python warnings come after thing trying to warn user about

I am using warnings in object code to alert users that something has happened but not halt the code. Below is a simple mockup based on the more complex scenarios I am encountering in my real code:

from warnings import warn
class myClass(object):        
    def __init__(self, numberArg):

        if numberArg > 1000:
            self._tmpTxt = "That's a really big number for this code." + \
                           "Code may take a while to run..."
            warn("\n%s %s" %(numberArg, self._tmpTxt))
            print("If this were real code:")
            print("Actions code takes because this is a big number would happen here.")

        print("If this were real code, it would be doing more stuff here ...")

mc1 = myClass(1001)

In my real code, when I instantiate the class which executes __init__(self, numberArg), the warning is output at the end after all the processing that follows the warning is done. Why does this happen?

More importantly, is there a way to ensure the warning is output first and then the rest of my code runs and delivers its output?

As with the example provided here, the desired effect is to alert the user what is going to happen before it happens, not afterwards, and deliver the output like a warning with warning format.

Note: This problem was encountered using Python 2.7 on iPython/Jupyter on a Windows 7 environment

like image 492
TMWP Avatar asked Apr 19 '17 20:04

TMWP


1 Answers

@direprobs provided the simplest answer to this question in the comments. Add this line of code after the call to warn().

sys.stderr.flush()

This code can be copied-and-pasted into Python 2.7 (Jupyter Notebooks) to quickly run it and see the effects:

Experiment One (for comparison to code that follows it):

# Note how warnings in this sample are held until after code is run and then output at the end ...

from warnings import warn
from warnings import resetwarnings

class myClass(object):        
    def __init__(self, numberArg):

        if numberArg > 1000:
            self._tmpTxt = "That's a really big number for this code." + \
                           "Code may take a while to run..."
            warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                                                       # possible categories (some of them):
                                                       # UserWarning, Warning, RunTimeWarning, ResourceWarning
                                                       # stacklevel was a experiment w/ no visible effect
                                                       # in this instance
            
            resetwarnings()                            # tried putting this before and after the warn()
            print("If this were real code:")
            print("Actions code takes because this is a big number would happen here.")
        
        print("If this were real code, it would be doing more stuff here ...")
        
mc1 = myClass(1001)

Experiment Two:

# In this case, we want the warning to come before code execution.  This is easily fixed as shown below.
# note: removed some extraneous useless stuff, the line to look for is sys.stderr.flush()

from warnings import warn
from warnings import resetwarnings
import sys

class myClass(object):        
    def __init__(self, numberArg):

        if numberArg > 1000:
            self._tmpTxt = "That's a really big number for this code." + \
                           "Code may take a while to run..."
            warn("\n%s %s" %(numberArg, self._tmpTxt), category=Warning)            
            sys.stderr.flush()                         # put this after each warn() to make it output more immediately
            print("If this were real code:")
            print("Actions code takes because this is a big number would happen here.")
        
        print("If this were real code, it would be doing more stuff here ...")
        
mc1 = myClass(1001)  

Experiment Three:

# code provided as an experiment ... may be updated later with a more useful example ...
# in theory, filterwarnings should help shake out repeat warnings if used with right arguments
#   * note how our loop causes the content to print twice, and in theory, the 3 instances of warnings
#   * occur twice each for 6 possible output warnings
#   * each new occurance (3 of them) still outputs, but when the same ones come up again, they don't
#   * we get 3 instead of 6 warnings ... this should be the effect of filterwarning("once")
#     in this instance

# help on this: https://docs.python.org/3/library/warnings.html#warning-filter
#               in this example:
#                  "once" arg = print only the first occurrence of matching warnings, regardless of location

from warnings import warn
from warnings import resetwarnings
from warnings import filterwarnings

class myClass(object):        
    def __init__(self, numberArg):
        
        for i in [1,2]:

            if numberArg > 1000:
                print("loop count %d:" %(i))
                self._tmpTxt = "That's a really big number for this code." + \
                               "Code may take a while to run..."
                filterwarnings("once")
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                sys.stderr.flush() # this provides warning ahead of the output instead of after it
                # resetwarnings()  # no noticeable effect on the code
                print("If this were real code:")
                print("Actions code takes because this is a big number would happen here.")

            if numberArg > 20000:
                self._tmpTxt = "That's a really really really big number for this code." + \
                               "Code may take a while to run..."                
                filterwarnings("once", "\nFW: %s %s" %(numberArg, self._tmpTxt))
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=0)
                # resetwarnings()  # no noticeable effect on the code
                sys.stderr.flush() # this provides warning ahead of the output instead of after it

            print("loop count %d:" %(i))    
            print("If this were real code, it would be doing more stuff here ...")

mc1 = myClass(1001)
print("====================")
mc2 = myClass(20001)

Look for this code on github later. Posted it here to help others investigating how to use warnings.

like image 163
TMWP Avatar answered Oct 23 '22 13:10

TMWP