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)
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)
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)
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)
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)
Final note: in earlier tests, we tried storing the return value from warn()
but the return value is just None.
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
In [13]:
ex_4(5,6) # no error to catch ... falls to else, then finally
Out[13]:
In [14]:
ex_4(9, 'x') # string = TypeError
In [15]:
ex_4(9,0) # ZeroDivisionError
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))
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)")
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)
Out[24]:
In [25]:
myTestFun(0)
Out[25]:
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')
In [ ]: