<jupyter_start><jupyter_text>What Is An Exception?[**Exceptions**](https://docs.python.org/3/tutorial/errors.html) are events that disrupt the normal flow of a program's instructions. It is basically a situation that Python cannot cope with. For example:  expecting one kind of input and it being different.An exception is an object that represents an error. When this object is raised, it not handled it will immediately terminate the program.When used properly, it allows you to perform better troubleshooting.(In Java, it is known as a try-catch block.)  How To Handle ExceptionsWhen you have code that you know could potentially break your script (like reading/writing to a file, end user input, etc) you can defend your program by using a **try** block with an **except** statement, followed by a block of code to handle the problem elegantly.Here is some example pseudo code:```pythontry:     do operation here (this is intended for a single line of code)    passexcept ExceptionTypeI:     Exception is a BaseException type...     but is bad practice to use this catch all!     This section of code will only run if this kind of Exception is hit    passexcept ExceptionTypeII:     This section of code will only run if this kind of Exception is hit    pass there can be multiple kinds of exceptionselse:     this block of code executes if no exception    pass``` Try this!```pythontry:    fh = open('textfile.txt', 'w')    fh.write('This is my test file for exception handling!!')except IOError:    print("ERROR:  can't find file or read data")else:    print("Written content in the file successfully.")    fh.close()```_What do you think will happen if you run the above code?_<jupyter_code>try:
    fh = open('textfile.txt', 'w')
    fh.write('This is my test file for exception handling!!')
except IOError:
    print("ERROR:  can't find file or read data")
else:
    print("Written content in the file successfully.")
    fh.close()<jupyter_output>Written content in the file successfully.<jupyter_text>Or how about this one?```pythontry:    fh = open('textfile.txt', 'r')    fh.write('This is my test file for exception handling!!')except IOError:    print("ERROR:  can't find file or read data")else:    print("Written content in the file successfully.")    fh.close()```<jupyter_code>try:
    fh = open('textfile.txt', 'w')
    fh.write('This is my test file for exception handling!!')
except IOError:
    print("ERROR:  can't find file or it's read-only data")
    fh.close()
else:
    print("Written content in the file successfully.")
    fh.close()<jupyter_output>Written content in the file successfully.<jupyter_text>What if you forgot the .txt?     What if you forgot one of the .close() calls? How might you improve the code?<jupyter_code>try:
    fh = open('textfile.txt', 'w')
    fh.write('This is my test file for exception handling!!')
except IOError:
    print("ERROR:  can't find file or it's read-only data")
else:
    print("Written content in the file successfully.")
finally:
    fh.close()<jupyter_output>Written content in the file successfully.<jupyter_text>Can You Have An **except** Clause With No Exceptions?If you had something like this:```pythontry:     do operation    passexcept:    do something    pass```This is "bad programming" because:- it catches ALL exceptions (so it's difficult to troubleshoot)- it does not identify the root cause of an issue that may occur- causes most scripts to have additional unintended bugs  Can You Have Multiple Exceptions?A single try statement can have many exception clauses.For example ...```pythontry:     do    passexcept Exception0 as e:     do stuff if Exception0 was triggered    passexcept (Exception1[, Exception2[,...ExceptionN]]):     If there is any of the exceptions listed in the list     do stuff in this section    passelse:     If no exception found, execute this block     else is not required though ... depends on needed logic    pass```  try-finally ClauseThe `finally` block of code is a place to put any code that MUST execute - regardless of a raised exception.If an exception is thrown, the code in the block is executed followed by the `finally` clause. It is mostly meant for cleaning up resources, including but not limited to:- closing files- closing network sockets- closing database connectionsClick on the image below to learn more about exceptions care of [RealPython](https://realpython.com).![alt text](https://files.realpython.com/media/try_except_else_finally.a7fac6c36c55.png "RealPython Intro To Exceptions")  Example Good --> BetterLet's say you create this file:```pythontry:    fh = open('testfile.txt', 'w')    fh.write('This is my test file for exception handlings!!!')except IOError:     do something then either gracefully catch or re-raise    raise     you can do this ... but there is more available to youfinally:     this happens always    print('Closing file...')    fh.close()```<jupyter_code>try:
    fh = open('testfile.txt', 'w')
    fh.write('This is my test file for exception handlings!!!')
except IOError:
    # do something then either gracefully catch or re-raise
    raise    # you can do this ... but there is more available to you
finally:
    # this happens always
    print('Closing file...')
    fh.close()<jupyter_output>Closing file...<jupyter_text>... and now you want to read from it! You CAN do it this way ...```pythontry:    fh = open('testfile.txt', 'r')    print(fh.readline()[:10])except IOError:     do something then either gracefully catch or re-raise    raise     you can do this ... but there is more available to youfinally:     this happens always    print('Closing file...')    fh.close()```<jupyter_code>try:
    fh = open('testfile.txt', 'r')
    print(fh.readline()[:10])
except IOError:
    # do something then either gracefully catch or re-raise
    raise    # you can do this ... but there is more available to you
finally:
    # this happens always
    print('Closing file...')
    fh.close()<jupyter_output>This is my
Closing file...<jupyter_text>... but what happens if the file isn't there?```pythontry:    fh = open('testfile-nonexist.txt', 'r')    print(fh.readline())except IOError as err:     do something then either gracefully catch or re-raise    print('IOError occurred:  {}'.format(repr(err)))    print('IOError occurred:  {}'.format(err))    raiseexcept FileNotFoundError:    print('File does not exist.')     raiseelse:     this only happens if there was no error with the open statement    fh.close()    print('File was closed!')```<jupyter_code>try:
    fh = open('testfile-nonexist.txt', 'r')
    print(fh.readline())
#except IOError as err:
#    # do something then either gracefully catch or re-raise
#    print('IOError occurred:  {}'.format(repr(err)))
#    print('IOError occurred:  {}'.format(err))
#    raise
except FileNotFoundError:
    print('File does not exist.')
    # raise
else:
    # this only happens if there was no error with the open statement
    fh.close()
    print('File was closed!')<jupyter_output>File does not exist.<jupyter_text>Just like it is "bad programming" to have a general Exception? If you have multiple statements in the same try you may have difficulty knowing exactly where you had the issue.This would be better:```pythonimport iotry:    fh = open('testfile.txt', 'r')    try:        fh.write('This is my test file for exception handlings!!!')    finally:         this happens always        print('Closing file...')        fh.close()except FileNotFoundError:    print('File does not exist.')     raiseexcept IOError as err:     do something then either gracefully catch or re-raise    print('IOError occurred:  {}'.format(repr(err)))    print('IOError occurred:  {}'.format(err))     raiseexcept io.UnsupportedOperation as err:     https://docs.python.org/3/library/io.htmlio.UnsupportedOperation    print('You are trying to write to a read only version of the file you opened.')```<jupyter_code>import io

try:
    fh = open('testfile.txt', 'r')
    try:
        fh.write('This is my test file for exception handlings!!!')
    finally:
        # this happens always
        print('Closing file...')
        fh.close()
except FileNotFoundError:
    print('File does not exist.')
    # raise
#except IOError as err:
#    # do something then either gracefully catch or re-raise
#    print('IOError occurred:  {}'.format(repr(err)))
#    print('IOError occurred:  {}'.format(err))
#    # raise
except io.UnsupportedOperation as err:
    # https://docs.python.org/3/library/io.html#io.UnsupportedOperation
    print('You are trying to write to a read only version of the file you opened.')<jupyter_output>Closing file...
You are trying to write to a read only version of the file you opened.<jupyter_text>POP QUIZWhy did this final one not include an error message like before?Was there even an error? Why or why not? If you've been following along, you've only seen the errors if the file doesn't already exist ... what would happen if:1. The file was there?2. The file existed but the `r` on file open was changed to `r+`?  Can I Make My Own Exception?Yup! The easiest way is to do something like this:```pythonx = 5if x >= 5:    raise Exception('Error message here.')```But you can read more on it:- [here](https://www.codementor.io/sheena/how-to-write-python-custom-exceptions-du107ufv9raising-exceptions-on-purpose) from CodeMentor- [here](http://www.tutorialspoint.com/python3/python_exceptions.htm) from TutorialsPoint- [here](https://docs.python.org/3/tutorial/errors.html) from official Python docs  The Argument Of An ExceptionAn excpetion's "argument" is a value that provides additional information about the problem - also referred to as the **exception object**. It is never a string or number, but it can hold this information as an attribute.The contents of an **exception object** vary by Exception type:- error string- error number- error locationHere is the pseudo code:```pythontry:     do something    passexception ExceptionType as Argument:     do other stuff    pass```**Temperature Change Example**```pythondef num_rtn(temp):    try:        return int(temp)    except ValueError as arg:        print("The argument does not contain numbers!\n", arg)num_rtn("abc")```What do you expect will happen?<jupyter_code>def num_rtn(temp):
    try:
        return int(temp)
    except ValueError as arg:
        print("The argument does not contain numbers!\n", arg)
num_rtn("abc")<jupyter_output>The argument does not contain numbers!
 invalid literal for int() with base 10: 'abc'<jupyter_text>Assertion StatementsWhen used at the beginning of a function, these are great for sanity checks to ensure the inputs are as expected. Though it is suggested to use this in testing, not production code.**Assertion statements** test an expression, that raises an [AssertionError](https://docs.python.org/3/library/exceptions.htmlAssertionError) if it renders `False`.This is meant more for a tool of the developers. For while you CAN catch the errors, they are **meant** to crash the program. In other words, if you do not catch them in a try-except statement they will terminate the program. Example With Try-Except```python ---------- Functions ----------def f1(x):    assert x == 1, "your_input != 1"  raised every time evaluates to falsedef f2(x):    f1(x)def f3(x):    f2(x)    not_cool ----------------------- try-except & assertion -----------------------input_str = input('Provide input:  ')try:    f1(int(input_str))  try different input to see if Exception is handled -------------------------------------------------------------- if you tried "except" without the "Exception as some_var_name" then you would not know what kind of exception it was --------------------------------------------------------------except Exception as err:   giving it a variable name to work with    print("caught exception:  {}". format(repr(err))) ------------------------------------ default except must be last in 3.6 ------------------------------------except:    print("Some stuff happened, right?")else:    print("no exception")finally:    print('The end! Here was your input:  {}'.format(input_str))```<jupyter_code># ----------
# Functions
# ----------
def f1(x):
    assert x == 1, "your_input != 1" # raised every time evaluates to false

def f2(x):
    f1(x)

def f3(x):
    f2(x)
    not_cool

# -----------------------
# try-except & assertion
# -----------------------
input_str = input('Provide input:  ')
try:
    f1(int(input_str)) # try different input to see if Exception is handled


# --------------------------------------------------------------
# if you tried "except" without the "Exception as some_var_name"
# then you would not know what kind of exception it was
# --------------------------------------------------------------
except Exception as err:  # giving it a variable name to work with
    print("caught exception:  {}". format(repr(err)))

# ------------------------------------
# default except must be last in 3.6
# ------------------------------------
except:
    print("Some stuff happened, right?")

else:
    print("no exception")
finally:
    print('The end! Here was your input:  {}'.format(input_str))<jupyter_output>Provide input:  1
no exception
The end! Here was your input:  1
{'fraction_non_alphanumeric': 0.13554716981132076, 'fraction_numerical': 0.0039245283018867925, 'mean_word_length': 3.274516129032258, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 30, 'https://': 8, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '163649', 'license': 'MIT', 'n_tokens_mistral': 3653, 'n_tokens_neox': 3535, 'n_words': 1541, 'path': 'CiscoDevNetCreate2019/Python_Basics_18_-_Exceptions___Assertions.ipynb', 'repo_name': 'ProsperousHeart/Training-Events', 'size': '13250'}