how to handle Exceptions (Errors) in programs
preview
These are the tests I have by the end of the chapter
1import src.exceptions
2import unittest
3
4
5class TestExceptions(unittest.TestCase):
6
7 def test_catching_module_not_found_error_in_tests(self):
8 with self.assertRaises(ModuleNotFoundError):
9 import does_not_exist
10
11 def test_catching_name_error_in_tests(self):
12 with self.assertRaises(NameError):
13 does_not_exist
14
15 def test_catching_attribute_error_in_tests(self):
16 with self.assertRaises(AttributeError):
17 src.exceptions.does_not_exist
18
19 def test_catching_type_error_in_tests(self):
20 with self.assertRaises(TypeError):
21 src.exceptions.function_name('the input')
22
23 def test_catching_index_error_in_tests(self):
24 a_list = [1, 2, 3, 'n']
25 with self.assertRaises(IndexError):
26 a_list[4]
27 with self.assertRaises(IndexError):
28 a_list[-5]
29
30 def test_catching_key_error_in_tests(self):
31 with self.assertRaises(KeyError):
32 {'key': 'value'}['does_not_exist']
33
34 def test_catching_zero_division_error_in_tests(self):
35 with self.assertRaises(ZeroDivisionError):
36 1 / 0
37
38 def test_catching_exceptions_in_tests(self):
39 with self.assertRaises(Exception):
40 raise Exception
41
42 def test_catching_exceptions_w_messages(self):
43 with self.assertRaisesRegex(
44 Exception, 'BOOM!!!'
45 ):
46 src.exceptions.raise_exception()
47
48 def test_catching_failure(self):
49 self.assertEqual(
50 src.exceptions.an_exception_handler(
51 src.exceptions.raise_exception
52 ),
53 'failed'
54 )
55
56 def test_catching_success(self):
57 self.assertEqual(
58 src.exceptions.an_exception_handler(
59 src.exceptions.does_not_raise_exception
60 ),
61 'succeeded'
62 )
63
64
65# Exceptions seen
66# AssertionError
67# ModuleNotFoundError
68# NameError
69# AttributeError
70# TypeError
71# IndexError
72# KeyError
73# ZeroDivisionError
questions about handling Exceptions
Here are questions you can answer after going through this chapter
requirements
how to test that an Exception is raised
open the project
I change directory to the
exceptionsfoldercd exceptionsthe terminal shows I am in the
exceptionsfolder.../pumping_python/exceptionsI activate the virtual environment
source .venv/bin/activateAttention
on Windows without Windows Subsystem for Linux use
.venv/bin/activate.ps1NOTsource .venv/bin/activate.venv/scripts/activate.ps1the terminal shows
(.venv) .../pumping_python/exceptionsI use
pytest-watchto run the testspytest-watchthe terminal shows
rootdir: .../pumping_python/exceptions collected 8 items tests/test_exceptions.py .... [100%] ============================ 8 passed in X.YZs =============================I hold ctrl on the keyboard and click on
tests/test_exceptions.pyto open it in the editor
test_catching_exceptions_w_messages
I can use the assertRaisesRegex method to test the message that is included with an Exception, this helps me to tell the difference when I have two Exceptions that have the same name
I add a failing test to
test_exceptions.py38 def test_catching_exceptions_in_tests(self): 39 with self.assertRaises(Exception): 40 raise Exception 41 42 def test_catching_exceptions_w_messages(self): 43 src.exceptions.raise_exception()the terminal shows AttributeError
AttributeError: module 'src.exceptions' has no attribute 'raise_exception'
GREEN: make it pass
I add the name to
exceptions.py1def function_name(): 2 return None 3 4 5raise_exceptionNameError: name 'raise_exception' is not definedI point it to None to define it
5raise_exception = NoneTypeError: 'NoneType' object is not callablewhen I make
raise_exceptiona function5def raise_exception(): 6 return Nonethe test passes
I want the function to raise Exception when it is called, I add assertRaises to the test in
test_exceptions.py42 def test_catching_exceptions_w_messages(self): 43 with self.assertRaises(Exception): 44 src.exceptions.raise_exception()the terminal shows AssertionError
AssertionError: Exception not raisedI add a raise statement to the
raise_exceptionfunction inexceptions.py5def raise_exception(): 6 raise Exceptionthe test passes
I can be more specific when testing for an Exception, I add assertRaisesRegex in
test_exceptions.py42 def test_catching_exceptions_w_messages(self): 43 with self.assertRaisesRegex( 44 Exception, 'BOOM!!!' 45 ): 46 src.exceptions.raise_exception()the terminal shows AssertionError
AssertionError: "BOOM!!!" does not match ""the assertRaisesRegex method checks that the code in its context raises the Exception it is given, with the message it is given. The default message of the Exception is the empty string (
'') and the test expects"BOOM!!!"the Exception is right, the message is not, I add the expected message in
exceptions.py5def raise_exception(): 6 raise Exception('BOOM!!!')the test passes. Time to add an Exception to the program
test_catching_failure
RED: make it fail
I add a new failing test in test_exceptions.py
42 def test_catching_exceptions_w_messages(self):
43 with self.assertRaisesRegex(
44 Exception, 'BOOM!!!'
45 ):
46 src.exceptions.raise_exception()
47
48 def test_catching_failure(self):
49 self.assertEqual(
50 src.exceptions.an_exception_handler(
51 src.exceptions.raise_exception
52 ),
53 'failed'
54 )
the terminal shows AttributeError
AttributeError: module 'src.exceptions' has no attribute 'an_exception_handler'
GREEN: make it pass
I add the name to
exceptions.py5def raise_exception(): 6 raise Exception('BOOM!!!') 7 8 9an_exception_handlerNameError: name 'an_exception_handler' is not definedI point it to None to define it
9an_exception_handler = NoneTypeError: 'NoneType' object is not callableI make it a function
9def an_exception_handler(): 10 return NoneTypeError: an_exception_handler() takes 0 positional arguments but 1 was givenI make the function take input
9def an_exception_handler(the_input): 10 return Nonethe terminal shows AssertionError
AssertionError: None != 'failed'the result of the call to
src.exceptions.an_exception_handleris None and the test expects'failed'I change the return statement to match the expectation
9def an_exception_handler(the_input): 10 return 'failed'the test passes.
test_catching_success
I want an_exception_handler to process its input and return failed when an Exception happens or return success when an Exception is NOT raised.
RED: make it fail
I add a new test to test_exceptions.py
48 def test_catching_failure(self):
49 self.assertEqual(
50 src.exceptions.an_exception_handler(
51 src.exceptions.raise_exception
52 ),
53 'failed'
54 )
55
56 def test_catching_success(self):
57 self.assertEqual(
58 src.exceptions.an_exception_handler(
59 src.exceptions.does_not_raise_exception
60 ),
61 'succeeded'
62 )
the terminal shows AttributeError
AttributeError: module 'src.exceptions' has no attribute 'does_not_raise_exception'
GREEN: make it pass
I add the name to
exceptions.py5def raise_exception(): 6 raise Exception('BOOM!!!') 7 8 9does_not_raise_exception 10 11 12def an_exception_handler(the_input): 13 return 'failed'NameError: name 'does_not_raise_exception' is not definedI point it to None to define it
9does_not_raise_exception = Nonethe terminal shows AssertionError
AssertionError: 'failed' != 'succeeded'src.exceptions.an_exception_handlerstill returns'failed', the test expects'succeeded'I make
an_exception_handler, remember the identity function9does_not_raise_exception = None 10 11 12def an_exception_handler(the_input): 13 return the_input 14 return 'failed'the terminal shows AssertionError
...::test_catching_failure - AssertionError: <function raise_exception at 0xabcd12e34567> != 'failed' ...::TestExceptions::test_catching_success - AssertionError: None != 'succeeded'I change the name of the input parameter to be clearer
12def an_exception_handler(a_function): 13 return a_function 14 return 'failed'I make
an_exception_handlerreturn the result of a call to its input as a function12def an_exception_handler(a_function): 13 return a_function() 14 return 'failed'a_function = None def an_exception_handler(a_function): > return a_function() E TypeError: 'NoneType' object is not callablebecause
does_not_raise_exceptionpoints to None, which is not callableI make it a function to make it callable
9def does_not_raise_exception(): 10 return None 11 12 13def an_exception_handler(a_function): 14 return a_function() 15 return 'failed'the terminal shows AssertionError
AssertionError: None != 'succeeded'the result of calling
src.exceptions.raise_exceptionintest_catching_failureis an Exception with a messageException: 'BOOM!!!'
I need a way for the function to choose what to do when an Exception is raised and when one is NOT raised. I can use the try statement to do this
how to use try…except…else
I add a try statement to
an_exception_handlerinexceptions.py13def an_exception_handler(a_function): 14 try: 15 a_function() 16 except Exception: 17 return 'failed'test_catching_failurepasses. The terminal still shows AssertionError fortest_catching_successAssertionError: None != 'succeeded'the try statement is used to handle Exceptions in programs
I add an else clause for when
a_function()runs without raising an Exception13def an_exception_handler(a_function): 14 try: 15 a_function() 16 except Exception: 17 return 'failed' 18 else: 19 return Nonethe terminal shows AssertionError
AssertionError: None != 'succeeded'this is still the same Exception and message
I change the return statement in the else clause
13def an_exception_handler(a_function): 14 try: 15 a_function() 16 except Exception: 17 return 'failed' 18 else: 19 return 'succeeded'the test passes.
The try statement is used to catch or handle Exceptions in Python. It allows the program to choose what it does when it runs into an Exception. I think of it as
tryrunning thisexcept Exception- when running this raisesException, run the code in this blockelse- when running this does NOT raiseException, run the code in this block
In this case
trycallinga_function()except Exception- when callinga_function()raisesExceptionreturn'failed'else- when callinga_function()does NOT raiseExceptionreturn'succeeded'
the try statement is how I think of Test Driven Development or the scientific method
Try something
if it fails, try something else
do this as many times as you can until you get what you want
or in the words of a famous singer …
I can be more specific with the Exception in the
exceptblock, for example13def an_exception_handler(a_function): 14 try: 15 a_function() 16 except ModuleNotFoundError: 17 return 'failed' 18 else: 19 return 'succeeded'the terminal shows Exception for
test_catching_failuresException: BOOM!!!!because Exception is not ModuleNotFoundError. The try statement only catches the Exception given in the
exceptblock and its children, all others are raisedI change it back to what works
13def an_exception_handler(a_function): 14 try: 15 a_function() 16 except Exception: 17 return 'failed' 18 else: 19 return 'succeeded'the terminal shows green again! I know how to test that an Exception is raised and how to handle Exceptions (Errors) in programs. I am a master!!
close the project
I close
exceptions.pyandtest_exceptions.pyin the editorI click in the terminal and exit the tests with ctrl+c on the keyboard, the terminal shows
(.venv) .../pumping_python/exceptionsI deactivate the virtual environment
deactivatethe terminal goes back to the command line,
(.venv)is no longer on the left side.../pumping_python/exceptionsI change directory to the parent of
exceptionscd ..the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory
review
I ran tests to show that
I can use the assertRaises method to catch Exceptions in tests and tested the following
I can use assertRaisesRegex to catch Exceptions with messages
I can use try..except…else to make programs that can choose what to do when Exceptions are raised
How many questions can you answer after going through this chapter?
code from the chapter
what is next?
you know
how to make a Python Test Driven Development environment automatically or how to make a Python Test Driven Development environment automatically on Windows without Windows Subsystem for Linux
Would you like to handle ZeroDivisionError in the Calculator?
rate pumping python
If this has been a 7 star experience for you, please leave a 5 star review. It helps other people get into the book too