Python [conda env:PY27_Test]

Python Warning and Exception Handling Basics

TOC

Working With Warnings

This example shows integration of warnings with object code. The following topics were useful in researching this code:


In [1]:
# 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=0, 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)


If this were real code:
Actions code takes because this is a big number would happen here.
If this were real code, it would be doing more stuff here ...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:11: RuntimeWarning: 
1001 That's a really big number for this code.Code may take a while to run...

In [2]:
# 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

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)


C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:12: Warning: 
1001 That's a really big number for this code.Code may take a while to run...
If this were real code:
Actions code takes because this is a big number would happen here.
If this were real code, it would be doing more stuff here ...

In [3]:
# note the warning from this output example to compare it to the next cell ...
# multiple identical warnings are issued and all are output

from warnings import warn

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..."
                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
                print("If this were real code:")
                print("Actions taken because this is a big number would happen here.")
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                sys.stderr.flush()
                
            if numberArg > 20000:
                self._tmpTxt = "That's a really really really big number for this code." + \
                               "Code may take a while to run..."                
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1)
                sys.stderr.flush() # this provides warning ahead of the output instead of after it

                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1)
                sys.stderr.flush()
                print("If this were real code:")
                print("Actions taken because this is an even bigger number would happen here.")

                
            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)


C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:13: RuntimeWarning: 
1001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:17: RuntimeWarning: 
1001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:13: RuntimeWarning: 
20001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:17: RuntimeWarning: 
20001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:22: UserWarning: 
20001 That's a really really really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:25: UserWarning: 
20001 That's a really really really big number for this code.Code may take a while to run...
loop count 1:
If this were real code:
Actions taken because this is a big number would happen here.
loop count 1:
If this were real code, it would be doing more stuff here ...
loop count 2:
If this were real code:
Actions taken because this is a big number would happen here.
loop count 2:
If this were real code, it would be doing more stuff here ...
====================
loop count 1:
If this were real code:
Actions taken because this is a big number would happen here.
If this were real code:
Actions taken because this is an even bigger number would happen here.
loop count 1:
If this were real code, it would be doing more stuff here ...
loop count 2:
If this were real code:
Actions taken because this is a big number would happen here.
If this were real code:
Actions taken because this is an even bigger number would happen here.
loop count 2:
If this were real code, it would be doing more stuff here ...

In [5]:
# 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 taken because this is a big number would happen here.")
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                sys.stderr.flush()
                # resetwarnings()  # no noticeable effect on the code
                
            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")    
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1)
                sys.stderr.flush() # this provides warning ahead of the output instead of after it
                # resetwarnings()  # no noticeable effect on the code

                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1)
                sys.stderr.flush()
                # resetwarnings()  # no noticeable effect on the code
                print("If this were real code:")
                print("Actions taken because this is an even bigger number would happen here.")

            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)


C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:18: RuntimeWarning: 
1001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:18: RuntimeWarning: 
20001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:30: UserWarning: 
20001 That's a really really really big number for this code.Code may take a while to run...
loop count 1:
If this were real code:
Actions taken because this is a big number would happen here.
loop count 1:
If this were real code, it would be doing more stuff here ...
loop count 2:
If this were real code:
Actions taken because this is a big number would happen here.
loop count 2:
If this were real code, it would be doing more stuff here ...
====================
loop count 1:
If this were real code:
Actions taken because this is a big number would happen here.
If this were real code:
Actions taken because this is an even bigger number would happen here.
loop count 1:
If this were real code, it would be doing more stuff here ...
loop count 2:
If this were real code:
Actions taken because this is a big number would happen here.
If this were real code:
Actions taken because this is an even bigger number would happen here.
loop count 2:
If this were real code, it would be doing more stuff here ...

In [7]:
# Notice the impact of putting the filter statement in only once at the start of the code:
# intermittent symptom:  repeat executions:
#                        sometimes produces output shown in this notebook
#                        sometimes produces identical output to the cell above

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

class myClass(object):        
    def __init__(self, numberArg):
        filterwarnings("once")    # note: message argument is regEx to filter by
                                  # in this case, we want it to filter all warnings
        
        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..."
                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 taken because this is a big number would happen here.")
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                sys.stderr.flush()
                # resetwarnings()  # no noticeable effect on the code
                
            if numberArg > 20000:
                self._tmpTxt = "That's a really really really big number for this code." + \
                               "Code may take a while to run..."
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1)
                sys.stderr.flush() # this provides warning ahead of the output instead of after it
                # resetwarnings()  # no noticeable effect on the code

                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1)
                sys.stderr.flush()
                # resetwarnings()  # no noticeable effect on the code
                print("If this were real code:")
                print("Actions taken because this is an even bigger number would happen here.")

            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)


C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:16: RuntimeWarning: 
1001 That's a really big number for this code.Code may take a while to run...
loop count 1:
If this were real code:
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:16: RuntimeWarning: 
20001 That's a really big number for this code.Code may take a while to run...
C:\ProgramFilesCoders\Anaconda2\envs\PY27_Test\lib\site-packages\ipykernel\__main__.py:27: UserWarning: 
20001 That's a really really really big number for this code.Code may take a while to run...
Actions taken because this is a big number would happen here.
loop count 1:
If this were real code, it would be doing more stuff here ...
loop count 2:
If this were real code:
Actions taken because this is a big number would happen here.
loop count 2:
If this were real code, it would be doing more stuff here ...
====================
loop count 1:
If this were real code:
Actions taken because this is a big number would happen here.
If this were real code:
Actions taken because this is an even bigger number would happen here.
loop count 1:
If this were real code, it would be doing more stuff here ...
loop count 2:
If this were real code:
Actions taken because this is a big number would happen here.
If this were real code:
Actions taken because this is an even bigger number would happen here.
loop count 2:
If this were real code, it would be doing more stuff here ...

Final note: in earlier tests, we tried storing the return value from warn() but the return value is just None.

Exception Handling

This section tests out different try-except-finally blocks followed by tests of their functionality to illustrate different things.


In [19]:
# this code is designed to illustrate the try-except-finally concept and general exception handling.
# it is not written for the real world:

class InvaidNegNumberError(Exception):
    # create own error type
    # source: https://www.codementor.io/sheena/how-to-write-python-custom-exceptions-du107ufv9
    def __init__(self,*args,**kwargs):
        Exception.__init__(self,*args,**kwargs)

def ex_4(a,b):
    try:
        if a < 0 or b < 0:
            rtnVal = abs(float(a)/b)    # moved here to head off error flagged in testing section
            raise InvaidNegNumberError("You entered a Negative number. " + 
                                       "This function or code is desinged for positive inputs.")
                                        # ths isn't realy an error, just assuming this function will
                                        # always return positive values to try something
        else:
            rtnVal = float(a)/b         # note: if we return here, a return in finally would 
                                        #       superced it somehow (tested this)

    except TypeError as te:
        print("You appear to have entered the wrong type of data.  Please input 2 numbers only")
        rtnVal = None
    except ZeroDivisionError as ze:
        print(ze)
        print("Please Enter b greater than 0 only: ex_4(a,b)")
        rtnVal = None
    except InvaidNegNumberError as ine:
        print(ine)
        print("Your numbers will be treated as positive and an answer will be returned.")
        # rtnVal assignment moved to try to debug an error
    except Exception as e:
        # catch all ... errors that I can't predict yet:
        print e
        print "Please revisit your inputs and try again."
        rtnVal = None
    else:
        print("You follow instructions well.  Good job.  Here is your answer:")
    finally:
        # executes regardless of whether there is an error or not
        print("Finally!  The end of the try-except block ...")
        
    print("This line is regular code in the program that executes after the try-except test is complete.")
    return rtnVal

Tests Of Different Asects of the Try-Except Block


In [13]:
ex_4(5,6)  # no error to catch ... falls to else, then finally


You follow instructions well.  Good job.  Here is your answer:
Finally!  The end of the try-except block ...
This line is regular code in the program that executes after the try-except test is complete.
Out[13]:
0.8333333333333334

In [14]:
ex_4(9, 'x')  # string = TypeError


You appear to have entered the wrong type of data.  Please input 2 numbers only
Finally!  The end of the try-except block ...
This line is regular code in the program that executes after the try-except test is complete.

In [15]:
ex_4(9,0)   # ZeroDivisionError


float division by zero
Please Enter b greater than 0 only: ex_4(a,b)
Finally!  The end of the try-except block ...
This line is regular code in the program that executes after the try-except test is complete.

In [18]:
print(round(ex_4(-5, 45),2))   # code raises this exception and then catches it
print(round(ex_4(-5, -45),2))  # exception is custom class:  InvaidNegNumberError
print(round(ex_4(5, -45),2))


You entered a Negative number. This function or code is desinged for positive inputs.
Your numbers will be treated as positive and an answer will be returned.
Finally!  The end of the try-except block ...
This line is regular code in the program that executes after the try-except test is complete.
0.11
You entered a Negative number. This function or code is desinged for positive inputs.
Your numbers will be treated as positive and an answer will be returned.
Finally!  The end of the try-except block ...
This line is regular code in the program that executes after the try-except test is complete.
0.11
You entered a Negative number. This function or code is desinged for positive inputs.
Your numbers will be treated as positive and an answer will be returned.
Finally!  The end of the try-except block ...
This line is regular code in the program that executes after the try-except test is complete.
0.11

In [17]:
# ex_4(0)
# ex_4(1/5)
# what this illustrates:
#    error occurs during function call so code is unable to catch it
#    would need try/except block around ex_4() to catch this error situation
#    we could do something like this ...
#    but as this is a programmer's error, this is really just for illustration
#    this type of error should be corrected in the code, not w/ try/except

try:
    ex_4(0/4)
except TypeError as te:
    print(te)
    print("Please enter 2 arguments of form:  ex_4(a, b)")


ex_4() takes exactly 2 arguments (1 given)
Please enter 2 arguments of form:  ex_4(a, b)

In [23]:
# getting a better handle on finally ... note this StackOverflow Post:
#   http://stackoverflow.com/questions/547791/why-use-finally-in-c
# although written for C, the explanation probably cuts across all languages using try-catch-finally or try-except-finally

# here's a test to show it in action

def myTestFun(someNum):
    try:
        x = 1000 / someNum
    except Exception as e:
        print(type(e))
        print(e)
        return someNum
    finally:
        print("This message needs to always display (it is presented under 'finally')")
    
    print("This message lives in the code but not under Finally.")
    return x

In [24]:
myTestFun(10)


This message needs to always display (it is presented under 'finally')
This message lives in the code but not under Finally.
Out[24]:
100

In [25]:
myTestFun(0)


<type 'exceptions.ZeroDivisionError'>
integer division or modulo by zero
This message needs to always display (it is presented under 'finally')
Out[25]:
0

Note: if try-except block throws an exception from within its processing, or returns from a function, then code after the try-except block never executes. Finally always executes.


In [26]:
# This example may be useful when tinkering with files:

def openfile(filename, mode):
    try:
        f = open(filename, mode)
        # do stuff
        f.close()
        
    except ValueError:
        print 'Likely to be wrong mode in this case.'
    except TypeError:
        print 'Parameter entered is wrong type: string for int, int for string, etc.'
    except IOError:
        print 'File doesn\'t exist in this case.'
    except Exception as e:
        print 'Some other error.'
        print 'System says: ', e
    else:
        # if no exceptions ...
        print 'No error'
    finally:
        # do last no matter what happens above (with or without errors does not matter):
        print 'Do this regardless of errors being encountered or not ...'

In [33]:
def testOpenFileTryExceptFun(fileNameString, mode):
    print("openfile('%s', '%s')" %(fileNameString, mode))
    print('-'*50)
    openfile(fileNameString, mode)
    print('\n')

testOpenFileTryExceptFun('nonexistent.txt', 'r')
testOpenFileTryExceptFun('data\existent.txt', 'no_such_mode')
testOpenFileTryExceptFun('data\existent.txt', 123)
testOpenFileTryExceptFun('data\existent.txt', 'r')


openfile('nonexistent.txt', 'r')
--------------------------------------------------
File doesn't exist in this case.
Do this regardless of errors being encountered or not ...


openfile('data\existent.txt', 'no_such_mode')
--------------------------------------------------
Likely to be wrong mode in this case.
Do this regardless of errors being encountered or not ...


openfile('data\existent.txt', '123')
--------------------------------------------------
Parameter entered is wrong type: string for int, int for string, etc.
Do this regardless of errors being encountered or not ...


openfile('data\existent.txt', 'r')
--------------------------------------------------
No error
Do this regardless of errors being encountered or not ...



In [ ]: