Exception handling in Python
Exception handling
Hierarchy of calls
main()
some_process()
for filename in some_list:
handle_file(filename)
private_module.deal_with_file(filename)
private_module._helper_function(filename)
public_module.process_file(filename)
with open(filename) as fh:
pass
Handling errors as return values
- Each function that fails returns some error indicator.
None
? An object that has and attribute "error"? None
would be bad as that cannot indicate different errors.- Every called needs to check if the function returned error. If at any point we forget our system might run with hidden failures.
def some_function()
result = do_something(filename)
if result:
do_something_else(result)
else:
return result
main()
...
result = some_function()
- If we forget to check the result and pass it on, we might get some error in the code that is quite far from where the error actually happened
main()
...
result = do_something(filename)
...
...
do_something_else(result)
- This can happen even if we don't pass the result around:
main()
...
do_something(filename)
...
...
do_something_else_assuming_the_other_worked()
Handling errors as exceptions
- Only need to explicitly check for it at the level where we know what to do with the problem.
- But: Do we want our pacemaker to stop totally after missing one beat? Probably not. Or better yet: not when it is in production.
main()
...
try:
...
result = do_something(filename)
do_something_else(result)
except Exception:
decide_what_to_do()
always_happens()
A simple exception
- ZeroDivisionError
When something goes wrong, Python throws (raises) an exception. For example, trying to divide a number by 0 won't work. If the exception is not handled, it will end the execution.
In some programming languages we use the expression "throwing an exception" in other languages the expression is "raising an exception". I use the two expressions interchangeably.
In the next simple example, Python will print the string before the division, then it will throw an exception, printing it to the standard error that is the screen by default. Then the script stops working and the string "after" is not printed.
def div(a, b):
print("before")
print(a/b)
print("after")
div(1, 0)
# before
# Traceback (most recent call last):
# File "examples/exceptions/divide_by_zero.py", line 6, in <module>
# div(1, 0)
# File "examples/exceptions/divide_by_zero.py", line 3, in div
# print(a/b)
# ~^~
# ZeroDivisionError: division by zero
Prevention
We might try to prevent the exceptions generated by the system, but even if succeed in preventing it, how do we indicate that there was an issue? For example with the input?
def div(a, b):
if b == 0:
# raise Exception("Cannot divide by zero")
print("Cannot divide by zero")
return None
print("before")
print(a/b)
print("after")
div(1, 0)
Working on a list
In a slightly more interesting example we have a list of values. We would like to divide a number by each one of the values.
As you can see one of the values is 0 which will generate and exception.
The loop will finish early.
def div(a, b):
print("dividing {} by {} is {}".format(a, b, a/b))
total = 100
values = [2, 5, 0, 4]
for val in values:
div(total, val)
# dividing 100 by 2 is 50.0
# dividing 100 by 5 is 20.0
# Traceback (most recent call last):
# ...
# ZeroDivisionError: division by zero
We can't repair the case where the code tries to divide by 0, but it would be nice if we could get the rest of the results as well.
Catch ZeroDivisionError exception
- except
- ZeroDivisionError
For that, we'll wrap the critical part of the code in a "try" block. After the "try" block we need to provide a list of exception that are caught by this try-block.
You could say something like "Try this code and let all the exceptions propagate, except of the ones I listed".
As we saw in the previous example, the specific error is called ZeroDivisionError.
If the specified exception occurs within the try: block, instead of the script ending, only the try block end and the except: block is executed.
import sys
def div(a, b):
print("dividing {} by {} is {}".format(a, b, a/b))
total = 100
values = [2, 5, 0, 4]
for val in values:
try:
div(total, val)
except ZeroDivisionError:
print("Cannot divide by 0", file=sys.stderr)
# dividing 100 by 2 is 50.0
# dividing 100 by 5 is 20.0
# Cannot divide by 0
# dividing 100 by 4 is 25.0
Module to open files and calculate something
Of course in the previous example, it would be probably much easier if we just checked if the number was 0, before trying to divide with it. There are many other cases when this is not possible. For example it is impossible to check if open a file will succeed, without actually trying to open the file.
In this example we open the file, read the first line which is a number and use that for division.
When the open() fails, Python throws an FileNotFoundError exception.
def read_and_divide(filename):
print("before " + filename)
with open(filename, 'r') as fh:
number = int(fh.readline())
print(100 / number)
print("after " + filename)
File for exception handling example
If we have a list of files and we would like to make sure we process as many as possible without any problem caused in the middle, we can catch the exception.
We have the following list of files. Notice that "two.txt" is missing and "zero.txt" has a 0 in it.
0
1
File two.txt is missing on purpose.
3
Open files - exception
import sys
import module
files = sys.argv[1:]
for filename in files:
module.read_and_divide(filename)
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# Traceback (most recent call last):
# ...
# ZeroDivisionError: division by zero
python open_list_of_files.py one.txt zero.txt two.txt three.txt
Handle divide by zero exception
- try
- except
- ZeroDivisionError
Running this code will the ZeroDivisionError exception, but it will die with a FileNotFoundError exception.
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError:
print(f"Cannot divide by 0 in file '{filename}'", file=sys.stderr)
print('')
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# Cannot divide by 0 in file 'zero.txt'
# before two.txt
# FileNotFoundError: [Errno 2] No such file or directory: 'two.txt'
python handle_divide_by_zero.py one.txt zero.txt two.txt three.txt
Handle files - exception
- FileNotFoundError
We can add multiple "except" statement at the end of the "try" block and handle several exceptions. Each one in a different way.
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError:
print(f"Cannot divide by 0 in file '{filename}'", file=sys.stderr)
except FileNotFoundError:
print(f"Cannot open file '{filename}'", file=sys.stderr)
print('')
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# Cannot divide by 0 in file 'zero.txt'
# before two.txt
# Cannot open file 'two.txt'
# before three.txt
# 33.333333333333336
# after three.txt
python handle_both_exceptions.py one.txt zero.txt two.txt three.txt
Catch all the exceptions and show their type
- Exception
We can also use the "except Exception" to catch all exceptions. In this case we might want to also print out the text and the type of the exception by ourselves.
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except Exception as err:
print(f" There was a problem in '{filename}'", file=sys.stderr)
print(f" Text: {err}", file=sys.stderr)
print(f" Name: {type(err).__name__}", file=sys.stderr)
print('')
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# There was a problem in 'zero.txt'
# Text: division by zero
# Name: ZeroDivisionError
# before two.txt
# There was a problem in 'two.txt'
# Text: [Errno 2] No such file or directory: 'two.txt'
# Name: FileNotFoundError
# before three.txt
# 33.333333333333336
# after three.txt
python show_exception_type.py one.txt zero.txt two.txt three.txt
List exception types
We can list more than one exceptions to be caught one after the other in a single "except" statement.
except (ZeroDivisionError, FileNotFoundError):
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except (ZeroDivisionError, FileNotFoundError) as err:
print(f"We have a problem with file '{filename}'", file=sys.stderr)
print(f"Exception type {err.__class__.__name__}", file=sys.stderr)
print('')
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# We have a problem with file 'zero.txt'
# Exception type ZeroDivisionError
# before two.txt
# We have a problem with file 'two.txt'
# Exception type FileNotFoundError
# before three.txt
# 33.333333333333336
# after three.txt
python handle_list_of_exceptions.py one.txt zero.txt two.txt three.txt
Hierarchy of Exceptions
There are many kinds of exceptions in Python and each module can define its own exception types as well. On this page you'll find the list and hierarchy of exceptions in Python.
Order of exception handling - bad
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except Exception as err:
print(f"General error {err}")
print(f"Error class: {err.__class__.__name__}")
except ZeroDivisionError:
print("ZeroDivisionError")
print(f"Cannot divide by 0 in file '{filename}'")
print('')
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# General error division by zero
# Error class: ZeroDivisionError
# before two.txt
# General error [Errno 2] No such file or directory: 'two.txt'
# Error class: FileNotFoundError
# before three.txt
# 33.333333333333336
# after three.txt
- Both exception are caught by the first
except
entry
Order of exception handling - good
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError:
print("ZeroDivisionError")
print(f"Cannot divide by 0 in file '{filename}'")
except Exception as err:
print(f"General error {err}")
print(f"Error class: {err.__class__.__name__}")
print('')
# before one.txt
# 100.0
# after one.txt
# before zero.txt
# ZeroDivisionError
# Cannot divide by 0 in file 'zero.txt'
# before two.txt
# General error [Errno 2] No such file or directory: 'two.txt'
# Error class: FileNotFoundError
# before three.txt
# 33.333333333333336
# after three.txt
- Always try to handle the more specific exceptions first
How to raise an exception
- raise
- throw
- Exception
As you create more and more complex applications you'll reach a point where you write a function, probably in a module, that needs to report some error condition. You can raise an exception in a simple way.
def add_material(name, amount):
if amount <= 0:
raise Exception(f"Amount of {name} must be positive. {amount} was given.")
print(f"Adding {name}: {amount}")
def main():
things_to_add = (
("apple", 3),
("sugar", -1),
("banana", 2),
)
for name, amount in things_to_add:
try:
add_material(name, amount)
except Exception as err:
print(f"Exception: {err}")
print("Type: " + type(err).__name__)
main()
$ python raise.py
Adding apple: 3
Exception: Amount of sugar must be positive. -1 was given.
Type: Exception
Adding banana: 2
Raise ValueError exception
- ValueError
You can be more specific with your error type and raise a ValueError.
def add_material(name, amount):
if amount <= 0:
raise ValueError(f"Amount of {name} must be positive. {amount} was given.")
print(f"Adding {name}: {amount}")
def main():
things_to_add = (
("apple", 3),
("sugar", -1),
("banana", 2),
)
for name, amount in things_to_add:
try:
add_material(name, amount)
except Exception as err:
print(f"Exception: {err}")
print("Type: " + type(err).__name__)
main()
$ python raise_value_error.py
Adding apple: 3
Exception: Amount of sugar must be positive. -1 was given.
Type: ValueError
Adding banana: 2
Stack trace of exceptions
import traceback
def bar():
foo()
def foo():
raise Exception("hi")
def main():
try:
bar()
except Exception as err:
track = traceback.format_exc()
print("The caught:\n")
print(track)
print("---------------------")
print("The original:\n")
bar()
main()
The caught:
Traceback (most recent call last):
File "stack_trace.py", line 11, in main
bar()
File "stack_trace.py", line 4, in bar
foo()
File "stack_trace.py", line 7, in foo
raise Exception("hi")
Exception: hi
---------------------
The original:
Traceback (most recent call last):
File "stack_trace.py", line 20, in <module>
main()
File "stack_trace.py", line 17, in main
bar()
File "stack_trace.py", line 4, in bar
foo()
File "stack_trace.py", line 7, in foo
raise Exception("hi")
Exception: hi
No need for exception to print Stack trace
- traceback
- format_stack
import traceback
def foo():
bar()
def bar():
#print(traceback.extract_stack())
print(''.join(traceback.format_stack()))
foo()
print("done")
# File "python/examples/other/print_stack_trace.py", line 10, in <module>
# foo()
# File "python/examples/other/print_stack_trace.py", line 4, in foo
# bar()
# File "python/examples/other/print_stack_trace.py", line 8, in bar
# print(''.join(traceback.format_stack()))
#
# done
Raise Exception from
- from
- None
- raise
import sys
def lower():
#print("starting lower")
raise Exception("exception in lower")
print("still here")
def upper(set_from):
try:
lower()
except Exception as err:
if set_from == "Default":
raise Exception("from upper")
elif set_from == "None":
raise Exception("from upper") from None
elif set_from == "Same":
raise Exception("from upper") from err
else:
raise Exception("from upper") from Exception("Incorrect input")
def main():
if len(sys.argv) != 2:
exit("Usage: raise_from.py [Default|None|Same]")
param = sys.argv[1]
# try:
# upper(param)
# except Exception as err:
# print("err:", err)
# print("cause:", err.__cause__)
# print("context:", err.__context__)
upper(param)
main()
Exercise: Exception int conversion
- In the earlier example we learned how to handle both ZeroDivisionError and FileNotFoundError exceptions. Now try this
cd examples/exceptions
python handle_both_exceptions.py one.txt zero.txt two.txt text.txt three.txt
before one.txt
100.0
after one.txt
before zero.txt
Cannot divide by 0 in file 'zero.txt'
before two.txt
Cannot open file 'two.txt'
before text.txt
Traceback (most recent call last):
File "handle_both_exceptions.py", line 9, in <module>
module.read_and_divide(filename)
File "/home/gabor/work/slides/python/examples/exceptions/module.py", line 4, in read_and_divide
number = int(fh.readline())
ValueError: invalid literal for int() with base 10: '3.14\n'
- This will raise a
ValueError
exception before handling file three.txt - Fix it by capturing the specific exception.
- Fix by capturing "all other exceptions".
3.14
Exercise: Raise Exception
- Write a function that expects a positive integer as its single parameter.
- Raise exception if the parameter is not a number.
- Raise a different exception if the parameter is not positive.
- Raise a different exception if the parameter is not whole number.
Solution: Exception int conversion (specific)
import sys
import module
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError:
print(f"Cannot divide by 0 in file '{filename}'")
except FileNotFoundError:
print(f"Cannot open file '{filename}'")
except ValueError as err:
print(f"ValueError {err} in file '{filename}'")
before one.txt
100.0
after one.txt
before zero.txt
Cannot divide by 0 in file zero.txt
before two.txt
Cannot open file two.txt
before text.txt
ValueError invalid literal for int() with base 10: '3.14\n' in file text.txt
before three.txt
33.333333333333336
after three.txt
python handle_3_exceptions.py one.txt zero.txt two.txt three.txt
Solution: Exception int conversion (all other)
import sys
import module
# python handle_both_exceptions.py one.txt zero.txt two.txt three.txt
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError:
print("Cannot divide by 0 in file {}".format(filename))
except IOError:
print("Cannot open file {}".format(filename))
except Exception as ex:
print("Exception type {} {} in file {}".format(type(ex).__name__, ex, filename))
before one.txt
100.0
after one.txt
before zero.txt
Cannot divide by 0 in file zero.txt
before two.txt
Cannot open file two.txt
before text.txt
Exception type ValueError invalid literal for int() with base 10: '3.14\n' in file text.txt
before three.txt
33.333333333333336
after three.txt
Solution: Raise Exception
def positive(num):
if type(num).__name__ == 'float':
raise Exception("The given parameter {} is a float and not an int.".format(num))
if type(num).__name__ != 'int':
raise Exception("The given parameter {} is of type {} and not int.".format(num, type(num).__name__))
if num < 0:
raise Exception("The given number {} is not positive.".format(num))
for val in [14, 24.3, "hi", -10]:
print(val)
print(type(val).__name__)
try:
positive(val)
except Exception as ex:
print("Exception: {}".format(ex))
Advanced Exception handling
Exceptions else
-
else
-
The else part will be execute after each successful "try". (So when there was no exception.)
import sys
import module
# python else.py one.txt zero.txt two.txt three.txt
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError as err:
print("Exception {} of type {} in file {}".format(err, type(err).__name__, filename))
else:
print("In else part after trying file {} and succeeding".format(filename))
# Will run only if there was no exception.
print()
Output:
before one.txt
100.0
after one.txt
In else part after trying file one.txt and succeeding
before zero.txt
Exception division by zero of type ZeroDivisionError in file zero.txt
before two.txt
Traceback (most recent call last):
File "else.py", line 9, in <module>
module.read_and_divide(filename)
File "/home/gabor/work/slides/python-programming/examples/exceptions/module.py", line 3, in read_and_divide
with open(filename, 'r') as fh:
FileNotFoundError: [Errno 2] No such file or directory: 'two.txt'
Exceptions finally
-
finally
-
We can add a "finally" section to the end of the "try" - "except" construct.
-
The code in this block will be executed after every time we enter the try.
-
When we finish it successfully. When we catch an exception. (In this case a ZeroDivisionError exception in file zero.txt" %}
-
Even when we don't catch an exception. Before the exception propagates up in the call stack, we still see the "finally" section executed.
import sys
import module
# python finally.py one.txt zero.txt two.txt three.txt
files = sys.argv[1:]
for filename in files:
try:
module.read_and_divide(filename)
except ZeroDivisionError as err:
print("Exception {} of type {} in file {}".format(err, type(err).__name__, filename))
finally:
print("In finally after trying file {}".format(filename))
print('')
Output:
before one.txt
100.0
after one.txt
In finally after trying file one.txt
before zero.txt
Exception division by zero of type ZeroDivisionError in file zero.txt
In finally after trying file zero.txt
before two.txt
In finally after trying file two.txt
Traceback (most recent call last):
File "finally.py", line 9, in <module>
module.read_and_divide(filename)
File "/home/gabor/work/slides/python-programming/examples/exceptions/module.py", line 3, in read_and_divide
with open(filename, 'r') as fh:
FileNotFoundError: [Errno 2] No such file or directory: 'two.txt'
Exit and finally
- finally
The "finally" part will be called even if we call "return" or "exit" in the "try" block.
def f():
try:
return
finally:
print("finally in f")
def g():
try:
exit()
finally:
print("finally in g")
print("before")
f()
print("after f")
g()
print("after g")
# before
# finally in f
# after f
# finally in g
Catching exceptions
- try
- except
- finally
def divide(x, y):
return x/y
def main():
cnt = 6
for num in [2, 0, 'a']:
try:
divide(cnt, num)
except ZeroDivisionError:
pass
except (IOError, MemoryError) as err:
print(err)
else:
print("This will run if there was no exception at all")
finally:
print("Always executes. {}/{} ended.".format(cnt, num))
print("done")
main()
Output:
This will run if there was no exception at all
Always executes. 6/2 ended.
Always executes. 6/0 ended.
Always executes. 6/a ended.
Traceback (most recent call last):
File "try.py", line 22, in <module>
main()
File "try.py", line 9, in main
divide(cnt, num)
File "try.py", line 3, in divide
return x/y
TypeError: unsupported operand type(s) for /: 'int' and 'str'
Home made exception
You can create your own exception classes that will allow the user to know what kind of an exception was caught or to capture only the exceptions of that type.
class MyException(Exception):
pass
def some():
raise MyException("Some Error")
def main():
try:
some()
except Exception as err:
print(err)
print("Type: " + type(err).__name__)
try:
some()
except MyException as err:
print(err)
main()
Output:
Some Error
Type: MyException
Some Error
Home made exception with attributes
class MyException(Exception):
def __init__(self, name, address):
self.name = name
self.address = address
def __str__(self):
return f'Have you encountered problems? name:{self.name} address:{self.address}'
def some():
raise MyException(name = "Foo Bar", address = "Somewhere deep in the code")
def main():
try:
some()
except MyException as err:
print(err.name)
print(err.address)
print(err)
print("Type: " + type(err).__name__)
except Exception as err:
print(f"Some other issue {err}")
main()
# Foo Bar
# Somewhere deep in the code
# Have you encountered problems? name:Foo Bar address:Somewhere deep in the code
# Type: MyException
Home made exception hierarcy
class MyError(Exception):
pass
class MyGreenError(MyError):
pass
class MyBlueError(MyError):
pass
def green():
raise MyGreenError('Hulk')
def blue():
raise MyBlueError('Frozen')
def red():
red_alert()
Home made exception hierarcy - 1
import colors as cl
def main():
print("start")
try:
cl.green()
except Exception as err:
print(err)
print(type(err).__name__)
print("done")
main()
Output:
start
Hulk
MyGreenError
done
Home made exception hierarcy - 2
import colors as cl
def main():
print("start")
try:
cl.green()
except cl.MyGreenError as err:
print(err)
print(type(err).__name__)
print("done")
main()
Output:
start
Hulk
MyGreenError
done
Home made exception hierarcy - 3
import colors as cl
def main():
print("start")
try:
cl.green()
except cl.MyError as err:
print(err)
print(type(err).__name__)
try:
cl.blue()
except cl.MyError as err:
print(err)
print(type(err).__name__)
try:
cl.red()
except cl.MyError as err:
print(err)
print(type(err).__name__)
print("done")
main()
Output:
start
Hulk
MyGreenError
Frozen
MyBlueError
Traceback (most recent call last):
File "hierarchy3.py", line 30, in <module>
main()
File "hierarchy3.py", line 19, in main
cl.red()
File "/home/gabor/work/slides/python/examples/exceptions/colors.py", line 18, in red
red_alert()
NameError: name 'red_alert' is not defined
Exercise: spacefight with exceptions
Take the number guessing game (or one-dimensional space-fight) and add exceptions for cases when the guess is out of space (0-200 by default), or when the guess is not a number.
import random
class Game:
def __init__(self):
self.lower_limit = 0
self.upper_limit = 200
self.number = random.randrange(self.lower_limit, self.upper_limit)
self.is_debug = False
self.running = True
def debug(self):
self.is_debug = not self.is_debug
def guess(self, num):
if num == 'd':
self.debug()
return
if self.is_debug:
print("Hidden number {}. Your guess is {}".format(self.number, num))
if num < self.number:
print("Too small")
elif num > self.number:
print("Too big")
else:
print("Bingo")
self.running = False
g = Game()
g.guess('d')
try:
g.guess('z')
except Exception as e:
print(e)
try:
g.guess('201')
except Exception as e:
print(e)
try:
g.guess('-1')
except Exception as e:
print(e)
Exercies: Raise My Exception
This is very similar to the exercise the first chapter about exceptions, but in this case you need to create your own hierarchy of exception classes.
- Write a function that expects a positive integer as its single parameter.
- Raise exception if the parameter is not a number.
- Raise a different exception if the parameter is not positive.
- Raise a different exception if the parameter is not whole number.
- In each case make sure both the text and the type of the exceptions are different.
- Include the actual value received as an attribute in the exception object.
Solution: spacefight with exceptions
import random
class SpaceShipError(Exception):
def __init__(self, inp):
self.inp = inp
class NumberTooBigError(SpaceShipError):
def __str__(self):
return "Number {} is too big".format(self.inp)
class NumberTooSmallError(SpaceShipError):
def __str__(self):
return "Number {} is too small".format(self.inp)
class NotANumberError(SpaceShipError):
def __str__(self):
return "Not a Number {}".format(self.inp)
class Game:
def __init__(self):
self.lower_limit = 0
self.upper_limit = 200
self.number = random.randrange(self.lower_limit, self.upper_limit)
self.is_debug = False
self.running = True
def debug(self):
self.is_debug = not self.is_debug
def guess(self, num):
if num == 'd':
self.debug()
return
if self.is_debug:
print("Hidden number {}. Your guess is {}".format(self.number, num))
try:
num = int(num)
except Exception:
raise NotANumberError(num)
if num > self.upper_limit:
raise NumberTooBigError(num)
if num < self.upper_limit:
raise NumberTooSmallError(num)
if num < self.number:
print("Too small")
elif num > self.number:
print("Too big")
else:
print("Bingo")
self.running = False
g = Game()
g.guess('d')
try:
g.guess('z')
except Exception as e:
print(e)
try:
g.guess('201')
except Exception as e:
print(e)
try:
g.guess('-1')
except Exception as e:
print(e)
#while g.running:
# guess = input("Please type in your guess: ")
# g.guess(int(guess))
Output:
Hidden number 137. Your guess is z
Not a Number z
Hidden number 137. Your guess is 201
Number 201 is too big
Hidden number 137. Your guess is -1
Number -1 is too small
Solution: Raise My Exception
class MyValueError(ValueError):
def __init__(self, val):
self.value = val
class MyFloatError(MyValueError):
def __str__(self):
return "The given parameter {} is a float and not an int.".format(self.value)
class MyTypeError(MyValueError):
def __init__(self, val, val_type):
self.value_type = val_type
super(MyTypeError, self).__init__(val)
def __str__(self):
return "The given parameter {} is of type {} and not int.".format(self.value, self.value_type)
class MyNegativeError(MyValueError):
def __str__(self):
return "The given number {} is not positive.".format(self.value)
def positive(num):
if type(num).__name__ == 'float':
raise MyFloatError(num)
if type(num).__name__ != 'int':
raise MyTypeError(num, type(num).__name__)
if num < 0:
raise MyNegativeError(num)
for val in [14, 24.3, "hi", -10]:
print(val)
print(type(val).__name__)
try:
positive(val)
except MyValueError as ex:
print("Exception: {}".format(ex))
print("Exception type {}".format(type(ex).__name__))
# Exception, ValueError
Exception finally return
def div(a, b):
try:
print("try")
c = a / b
except Exception:
print("exception")
return
finally:
print("finally")
div(2, 1)
print('---')
div(2, 0)