#!/usr/bin/python3 # -*- coding: utf-8 -*- from typing import Sequence, Any # %% Exception handling # Exceptions occur for these statements (what types?) while True print("Hello world"): 10 * (1/0) "2" + 2 Print("hello world") print "hello world" print(hello world) d = {1: 1, 2: 2} d[3] li = [1, 2, 3] li[4] """ The parser repeats the offending line and displays a little ‘arrow’ pointing at the earliest point in the line where the error was detected. The error is caused by (or at least detected at) the token PRECEDING the arrow! The last line of the error message indicates what happened. """ # assertions """ An assertion is a sanity-check that you can turn on or turn off, when you are done with your testing of the program. An expression is tested, and if the result comes up false, an exception is raised. Assertions are carried out by the assert statement. Programmers often place assertions at the start of a function to check for valid input, and after a function call to check for valid output. General form: assert thing_that_should_be_true, "error message" """ def KelvinToFahrenheit(Temperature: float) -> float: assert Temperature >= 0, "Not cool... that's colder than absolute zero!" return ((Temperature - 273) * 1.8) + 32 print(KelvinToFahrenheit(273)) print(int(KelvinToFahrenheit(505.78))) print(KelvinToFahrenheit(-5)) # assert does not need its bool condition in parentheses, but it can (not the error) numbers = [1.5, 2.3, 0.7, -0.001, 4.4] total = 0.0 for n in numbers: assert n > 0.0, "Data should only contain positive values" total += n print("total is:", total) # Full exception handling # It's often better to ask forgiveness than permission: def print_sorted(collection: Sequence[Any]) -> None: try: collection.sort() except Exception as e: print("Passed Duck does not quack, i.e., does not have .sort()", e) print(collection) myarr = [1, 0, 3] print_sorted(myarr) # set's are not mutable, and could not have a sort method: myset = set(myarr) print_sorted(myset) # Note: You sanitized inputs before. # An additional way to do input-satinization, with better errors while True: try: n_str = input("Please enter an integer: ") n = int(n_str) print("hey") break # Exception is a big super-category of exceptions, # when you don't know the type to catch except Exception as e: print("No valid integer! Please try again ...", e) print("Great, you successfully entered an integer!") """ The try statement works as follows. * First, the try clause is executed (the statement(s) between the try and except keywords). * If no exception occurs, the except clause is skipped, and execution of the try statement is finished. * If an exception occurs during execution of the try clause, the rest of the clause is skipped. Then if its type matches the exception named after the except keyword, the except clause is executed, and then execution continues after the try statement. * If an exception occurs which does not match the exception named in the except clause, it is passed on to outer try statements; if no handler is found, it is an unhandled exception, and execution stops with a message as shown above. A try statement may have more than one except clause, to specify handlers for different exceptions. At most one handler (except) will be executed/entered (the first to match). Handlers only handle exceptions that occur in the corresponding try clause, not in other handlers of the same try statement. """ # +++++++++++++++++++++++++++ Cahoot-17.1! # https://mst.instructure.com/courses/58101/quizzes/56735 # Rather than Exception, # Use more specific categories of except (catch), # if you need to anyway: while True: try: n = input("Please enter an integer: ") n = int(n) break except ValueError as e: print("No valid integer! Please try again ...", e) print("Great, you successfully entered an integer!") # You don't need to specify a type of exception to catch in an except block: def temp_convert_worst(var: Any) -> int: try: return int(var) except: print("The argument does not contain numbers:\n ") temp_convert_worst("xyz") # capture the exception in big super-category def temp_convert_ok(var: Any) -> int: try: return int(var) except Exception: print("The argument does not contain numbers:\n ") temp_convert_ok("xyz") # and actually save it as an object to print: def temp_convert_better(var: Any) -> int: try: return int(var) except Exception as you_choose_the_name: print("The argument does not contain numbers:\n ", you_choose_the_name) # Call above function here. temp_convert_better("xyz") # finally: bock # is a way to get code to execute after exception blocks, NO MATTER WHAT. # that you also want to execute after normal behavior # Often, this is cleanup code. try: x = float(input("Your number: ")) inverse = 1.0 / x finally: print("There may or may not have been an exception.") print("Is finally: really that useful, because this prints no matter what too?") print("The inverse: ", inverse) # example combining the above try: x = float(input("Your number: ")) inverse = 1.0 / x except ValueError as e: print("You should have given either an int or a float", e) except ZeroDivisionError as e: print("Infinity", e) exit(0) finally: print("There may or may not have been an exception.") print("Does this also prints no matter what?") # This shows another way that a finally clause is useful # else clauses on the other hand: # are hardly necessary (see below example) def divide(x, y): try: result = x / y except ZeroDivisionError: print("division by zero!") return else: print("result is", result) finally: print("executing finally clause") print("What about this") divide("2", "0") divide(2, 1) divide(2, 0) # +++++++++++++++++++++++++++++ Cahoot-17-2! # https://mst.instructure.com/courses/58101/quizzes/56736 # A more natural alternative to else clause def divide(x, y): try: result = x / y print("result is", result) except ZeroDivisionError: print("division by zero!") finally: print("executing finally clause") divide("2", "0") divide(2, 1) divide(2, 0) """ However, regarding the use of try/else: It is less likely to result in catching an exception that you did not intend to catch.... a subtle benefit, rarely worth the cost. It's a matter of style: https://stackoverflow.com/questions/855759/python-try-else#855764 """ # You can catch multiple types in one except try: x = float(input("Your number: ")) inverse = 1.0 / x except (ValueError, ZeroDivisionError): print("You broke something with numbers!") # It's often best to just do the exception handling (try/catch) in main: def this_fails() -> None: x = 1 / 0 try: this_fails() except ZeroDivisionError as err: print("Handling run-time error:", err) # if you catch in a function, and want to raise it back to main: def filter_name(name): """This is not generally the purpose of try/catch""" try: name = name.encode("ascii") except UnicodeError as e: if name == "Gaël": print("OK, Gaël") else: raise e return name try: filter_name("Gaël") except Exception: print("did not work") try: filter_name("Stéfan") except Exception as e: print("did not work", e) # Summary # Handle exceptions with a try/except block try: # Optionally use "raise" to raise a standard python Exception class raise IndexError("This is an index error") # Or assume one would be thrown by python x = 1 / 0 except IndexError as e: print(e) except (TypeError, NameError) as e: # Multiple exceptions can be handled together, if required. print(e) except Exception as e: print("something else happened: ", e) else: # Optional clause to the try/except block. # Must follow all except blocks print("All good!") # Runs only if the code in try raises no exceptions # Might as well just put this at the bottom of the try, or after it all. finally: # Execute under ALL circumstances # Noteably including exit()/return in excepts above print("We can clean up resources here") # Instead of try/finally to cleanup resources you can use a with statement with open("myfile.txt") as f: for line in f: print(line) # More to come on this later in file i/o. # More details on raise """ The raise statement allows the programmer to force a specified stardard python exception, for example: """ raise NameError("HiThere") """ An Exception is an object, a class, like all others in python. When it is created, it's constructor/__init__ is used, as above: NameError(...) When an exception occurs, it may have an associated value, also known as the exception’s argument (an argument to __init__ anyway). The presence and type of the argument depend on the exception type. The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in instance.args. For convenience, the exception instance defines __str__() so the arguments can be printed directly without having to reference .args. One may also instantiate an exception first before raising it, and add any attributes to it as desired. """ # raise throws a python stardard Exception class object instance: try: raise Exception("spam", "eggs") except Exception as inst: print(type(inst)) # the exception instance print(inst.args) # arguments stored in .args print(inst) # __str__ allows args to be printed directly, # but may be overridden in exception subclasses x, y = inst.args # unpack args print("x =", x) print("y =", y) """ You can use what we learned previously in classes and inheritance to create your own exception class. This is done by creating a new class object, and inheriting a built-in python class, Exception. Custom exceptions inherited from the standard python Exception """ class MyException(Exception): pass raise MyException("An exception doesn't always prove the rule!") # The special Exception class is not the only that can be raised. """ A class in an except clause is compatible with an exception, if it is the same class or a base class thereof (but not the other way around; an except clause listing a derived class is not compatible with a base class). """ # ++++++++++ Cahoot-17.3 # https://mst.instructure.com/courses/58101/quizzes/56737 class B(Exception): pass class C(B): pass class D(C): pass for my_cls in [B, C, D]: try: raise my_cls() except D: print("D") except C: print("C") except B: print("B") # +++++++++++++++++++ Cahoot-17.4 # https://mst.instructure.com/courses/58101/quizzes/56738 # Inheritance and catching? for my_cls in [B, C, D]: try: raise my_cls() except B: print("B") except C: print("C") except D: print("D") # Catching children can be slippery work, watch out, lest you drop them! # Inheritance and catching? for my_cls in [B, C, D]: try: raise my_cls() except Exception: print("stuff") except B: print("B") except C: print("C") except D: print("D") # Nested exceptions try: try: raise NameError("HiThere") except NameError: print("An exception flew by!") 1 / 0 except Exception as E: print(E) print("here?") """ If you need to determine whether an exception was raised, but don’t intend to handle it, a simpler form of the raise statement allows you to re-raise the exception: """ try: try: raise NameError("HiThere") except NameError: print("An exception flew by!") raise except Exception as E: print(E) print("here?") # some interesting exceptions try: text = input("Enter something --> ") except EOFError: print("Why did you do an EOF on me?") except KeyboardInterrupt: print("You cancelled the operation.") else: print("You entered {}".format(text)) """ press/enter: ctrl-d ctrl-c type something else """