#set document(title: "14.4 Handling exceptions", author: "OpenStax / XYZ Homework") #set page(width: 8.5in, height: auto, margin: 1in) #import "@preview/cetz:0.5.2" #set text(font: ("STIX Two Text", "Libertinus Serif", "New Computer Modern"), size: 10.5pt, lang: "en") #show math.equation: set text(font: ("STIX Two Math", "New Computer Modern Math")) #set par(justify: true, leading: 0.62em, spacing: 0.9em) #set enum(spacing: 1.1em) // room between list items so tall inline fractions don't collide #set list(spacing: 1.1em) #set table(stroke: 0.5pt + rgb("#c7ccd3")) #let BLUE = rgb("#183B6F") // brand navy — section bars + example/solution labels (white on navy 11.09:1) #let ORANGE = rgb("#A94509") // brand primary-700 — AA-safe deep orange for TEXT (5.93:1 on white; raw brand #F37021 is 2.94:1 and must never carry text) #let RED = rgb("#DC2626") // brand error-600 #let GREEN = rgb("#059669") // brand success-600 (decoration only; small green text uses green-text #007942) #show heading.where(level: 1): it => block(width: 100%, above: 0pt, below: 16pt, fill: gradient.linear(BLUE, rgb("#2C5AA0")), inset: (x: 14pt, y: 12pt), radius: 3pt, text(fill: white, weight: "bold", size: 19pt, it.body)) #show heading.where(level: 2): it => block(width: 100%, above: 18pt, below: 10pt, fill: BLUE, inset: (x: 10pt, y: 6pt), radius: 2pt, text(fill: white, weight: "bold", size: 12pt, it.body)) #show heading.where(level: 3): it => text(fill: ORANGE, weight: "bold", size: 12.5pt, it.body) #show heading.where(level: 4): it => text(fill: BLUE, weight: "bold", size: 10.5pt, it.body) #let examplebox(label, title, body) = block(width: 100%, breakable: true, fill: rgb("#EFF1F5"), stroke: 0.5pt + rgb("#CFDDF0"), radius: 4pt, inset: 10pt, above: 12pt, below: 12pt)[ #block(below: 6pt)[#box(fill: BLUE, inset: (x: 6pt, y: 2pt), radius: 2pt, text(fill: white, weight: "bold", size: 8.5pt, label)) #h(0.4em) #strong[#title]] #body] // rail = decorative left rule (raw brand token); labelcolor = AA-safe label text shade #let notebox(label, rail, labelcolor, tint, body) = block(width: 100%, breakable: true, fill: tint, stroke: (left: 3pt + rail), inset: (left: 10pt, rest: 8pt), radius: (right: 4pt), above: 11pt, below: 11pt)[ #text(fill: labelcolor, weight: "bold", size: 7.5pt, tracking: 0.5pt)[#upper(label)] #linebreak() #body] #let solutionbox(body) = block(above: 4pt, below: 8pt)[ #text(fill: BLUE, weight: "bold", size: 8.5pt)[Solution] #linebreak() #body] #let figph(msg) = block(width: 100%, height: 60pt, fill: rgb("#f6f7f9"), stroke: (paint: rgb("#c7ccd3"), dash: "dashed"), radius: 4pt, inset: 10pt)[ #align(center + horizon, text(fill: rgb("#889"), style: "italic", size: 9pt, msg))] // Standardize inlined figure sizes: measure the natural CeTZ canvas, then scale to a // consistent envelope (aspect-aware; see build_typst.py FIG_* constants). Unlike the // print preamble, dimensions are FLOORED: in an editor a user can trim a figure to a // degenerate 1-D shape (a bare line), and w/h or tw/w would then divide by zero. #let _STD_W = 3.5 #let _WIDE_W = 5.6 #let _MAX_H = 3.4 #let _ASPECT_WIDE = 2.2 #let _UPSCALE_MAX = 1.15 #let stdfig(body) = context { let m = measure(body) let w = calc.max(m.width / 1in, 0.01) let h = calc.max(m.height / 1in, 0.01) let tw = if w / h > _ASPECT_WIDE { _WIDE_W } else { _STD_W } let s = calc.min(tw / w, _MAX_H / h, _UPSCALE_MAX) align(center, box(scale(x: s * 100%, y: s * 100%, reflow: true, body))) } #show figure: set block(breakable: false) #set figure(gap: 8pt) #show figure.caption: set text(size: 8.5pt, fill: rgb("#555")) == 14.4#h(0.6em)Handling exceptions === Learning objectives By the end of this section you should be able to - Describe two exceptions that may occur when reading files. - Write try/except statements that handle built-in exceptions. === Runtime errors Various errors may occur when reading a file: - FileNotFoundError: The filename or path is invalid. - IndexError/ValueError: The file's format is invalid. - Other errors caused by invalid contents of a file. When an error occurs, the program terminates with an error message. #examplebox("Example 1")[Typo in a file][ A file named food\_order.txt has the following contents: 5 sandwiches 4 chips 1 pickle soft drinks The following program expects each line of the file to begin with an integer: for line in open("food\_order.txt"):     space = line.index(" ")     qty = int(line\[:space\])     item = line\[space+1:-1\]     print(qty, item) Unfortunately, the line "soft drinks" does not begin with an integer. As a result, the program terminates and displays an error message: Traceback (most recent call last):   File "food\_order.py", line 3     qty = int(line\[:space\]) ValueError: invalid literal for int() with base 10: 'soft' ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Common exceptions] What error might occur in each situation? ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Exploring further] The #link("https://openstax.org/r/100exceptions")[Built-in Exceptions] page of the Python Standard Library explains the meaning of each exception. ] === Try and except Programs can be designed to handle exceptions, rather than terminate. A #strong[try] statement runs code that might raise an exception. An except clause runs code in response to the exception. #examplebox("Example 2")[Try to open a file][ The following program, named #emph[try\_open.py], asks the user for a filename and counts the number of lines in the file. name = input("Enter a filename: ") try:     file = open(name)     lines = file.readlines()     count = len(lines)     print(name, "has", count, "lines") except FileNotFoundError:     print("File not found:", name) print("Have a nice day!") When running this program with the input #emph[try\_open.py], the name of the program file, the output is: Enter a filename: try\_open.py try\_open.py has 9 lines Have a nice day! If the filename does not exist, a FileNotFoundError is raised on line 3. The program then jumps to the except clause on line 7 and continues to run. The resulting output is: Enter a filename: try\_open.txt File not found: try\_open.txt Have a nice day! ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Predicting output with exceptions] For each code snippet, what is the output? ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[Type analyzer] Analysis programs often need to find numbers in large bodies of text. How can a program tell if a string like "123.45" represents a number? One approach is to use exceptions: + Try converting the string to an integer. If no ValueError is raised, then the string represents an integer. + Otherwise, try converting the string to a float. If no ValueError is raised, then the string represents a float. + Otherwise, the string does not represent a number. Implement the get\_type() function using this approach. The provided main block calls get\_type() for each word in a file. get\_type() should return either "int", "float", or "str", based on the word. The output for the provided data.txt is: str: Hello int: 100 str: times! float: 3.14159 str: is str: pi. def get\_type(word): \# TODO return "int", "float", or "str" if \_\_name\_\_ == "\_\_main\_\_": for line in open("data.txt"): for word in line.split(): print(get\_type(word), word, sep=": ") ] #notebox("Note", rgb("#8a94a6"), rgb("#556666"), rgb("#f7f8fa"))[ #emph[United countries] Write a program that prompts the user to input a word and a filename. The program should print each line of the file that contains the word. Here is an example run of the program (user input in bold): Enter a word: #strong[United] Enter a filename: #strong[countries.csv] United Arab Emirates,9890402,83600,118 United Kingdom,67886011,241930,281 United States of America,331002651,9147420,36 This example uses a file named countries.csv based on the #link("https://openstax.org/r/100countries")[alphabetical list of countries] from Worldometer. Each line of the file includes a country's name, population, land area, and population density, separated by commas. The user might incorrectly type the filename (Ex: #emph[countries.txt] instead of #emph[countries.csv]). Your program should output an error message if the file is not found, and keep prompting the user to input a filename until the file is found: ... Enter a filename: #strong[countries] File not found: countries Enter a filename: #strong[countries.txt] File not found: countries.txt Enter a filename: #strong[countries.csv] ... Hint: Try to open the file specified by the user. A FileNotFoundError is raised if the filename is invalid. word = input("Enter a word: ") \# Step 1: Prompt for a filename until the file is found. \# Step 2: Look for the word on each line of the file. ]