generated from PovertyAction/ipa-python-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path09-errors.qmd
More file actions
547 lines (429 loc) · 16.9 KB
/
09-errors.qmd
File metadata and controls
547 lines (429 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
---
title: "Errors and Exceptions"
abstract: |
Learn to understand and handle Python errors effectively. Master reading tracebacks, identifying common error types, and debugging techniques to resolve syntax, name, index, and other common programming errors.
date: last-modified
format:
html: default
authors-ipa:
- "[Author Name](https://poverty-action.org/people/author_name)"
contributors:
- "[Contributor Name](https://poverty-action.org/people/contributor_name)"
keywords: ["Python", "Error Handling", "Debugging", "Exceptions", "Troubleshooting", "Tutorial"]
license: "CC BY 4.0"
---
::: {.callout-note}
## Learning Objectives
- To be able to read a traceback, and determine where the error took place and what type it is.
- To be able to describe the types of situations in which syntax errors, indentation errors, name errors, index errors, and missing file errors occur.
## Questions
- How does Python report errors?
- How can I handle errors in Python programs?
:::
Every programmer encounters errors,
both those who are just beginning,
and those who have been programming for years.
Encountering errors and exceptions can be very frustrating at times,
and can make coding feel like a hopeless endeavour.
However,
understanding what the different types of errors are
and when you are likely to encounter them can help a lot.
Once you know *why* you get certain types of errors,
they become much easier to fix.
Errors in Python have a very specific form,
called a [traceback](../learners/reference.md#traceback).
Let's examine one:
```python
# This code has an intentional error. You can type it directly or
# use it for reference to understand the error message below.
def favorite_ice_cream():
ice_creams = [
'chocolate',
'vanilla',
'strawberry'
]
print(ice_creams[3])
favorite_ice_cream()
```
```error
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-1-70bd89baa4df> in <module>()
9 print(ice_creams[3])
10
---> 11 favorite_ice_cream()
<ipython-input-1-70bd89baa4df> in favorite_ice_cream()
7 'strawberry'
8 ]
----> 9 print(ice_creams[3])
IndexError: list index out of range
```
This particular traceback has two levels.
You can determine the number of levels by looking for the number of arrows on the left hand side.
In this case:
1. The first shows the context for the higher level, that is, the function that was called.
In this case this is line 11, the call to `favorite_ice_cream()`.
2. The second shows the context for the error,
that is, what function was being executed when the error occurred.
In this case this is line 9, `print(ice_creams[3])`.
Both levels of the traceback show the *filename*, the *line number*,
and the *line of code* where the problem occurred.
In this case the filename was `<ipython-input-1-70bd89baa4df>`.
This is the special filename that PyCharm and other interactive environments use for
code typed directly into the interactive command line.
The last level is the actual *error message*.
Long tracebacks can be intimidating and sometimes hard to read,
but if you start at the bottom and read up,
you can understand what happened that caused the error.
## Syntax Errors
When you forget a colon at the end of a line,
accidentally add one space too many when indenting under an `if` statement,
or forget a parenthesis,
you will encounter a [syntax error](../learners/reference.md#syntax-error).
This means that Python couldn't figure out how to read your program.
This is similar to forgetting punctuation in English:
for example,
this text is difficult to read there is no punctuation there is also no capitalization
why is this hard to read
```python
def some_function()
msg = 'hello world'
print(msg)
return msg
```
```error
File "<ipython-input-3-6bb841ea1423>", line 1
def some_function()
^
SyntaxError: invalid syntax
```
The message indicates a problem on first line of our function definition, and the `^` points to the problem area.
In this case the problem is that the function definition is missing the colon at the end.
Actually, the function above has *two* issues with syntax.
If we fix the first issue:
```python
def some_function():
msg = 'hello world'
print(msg)
return msg
```
```error
File "<ipython-input-4-ae290e7659cb>", line 4
return msg
^
IndentationError: unindent does not match any outer indentation level
```
Both `SyntaxError` and `IndentationError` indicate a problem with the syntax of your program,
but an `IndentationError` is more specific:
it *always* means that there is a problem with how your code is indented.
::: {.callout-note}
## Tabs and Spaces
Some indentation errors are harder to spot than others.
In particular, mixing spaces and tabs can be difficult to spot
because they are both [whitespace](../learners/reference.md#whitespace).
In the example below, the first two lines in the body of the function
`some_function` are indented with tabs, while the third line — with spaces.
If you're working in a Jupyter notebook, be sure to copy and paste this example
rather than trying to type it in manually because Jupyter automatically replaces
tabs with spaces.
```python
def some_function():
msg = 'hello world'
print(msg)
return msg
```
Visually it is impossible to spot the error.
Fortunately, Python does not allow you to mix tabs and spaces.
```error
File "<ipython-input-5-653b36fbcd41>", line 4
return msg
^
IndentationError: inconsistent use of tabs and spaces in indentation
```
:::
## Variable Name Errors
Another very common type of error is called a `NameError`,
and occurs when you try to use a variable that does not exist.
For example:
```python
print(a)
```
```error
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-7-9d7b17ad5387> in <module>()
----> 1 print(a)
NameError: name 'a' is not defined
```
Variable name errors come with some of the most informative error messages,
which are usually of the form "name 'the_variable_name' is not defined".
Why does this error message occur?
That's a harder question to answer,
because it depends on what your code is supposed to do.
However,
there are a few very common reasons why you might have an undefined variable.
The first is that you meant to use a
[string](../learners/reference.md#string), but forgot to put quotes around it:
```python
print(hello)
```
```error
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-8-9553ee03b645> in <module>()
----> 1 print(hello)
NameError: name 'hello' is not defined
```
The second is that you might be trying to use a variable that does not yet exist.
In the following example,
`count` should have been defined (e.g., with `count = 0`) before the for loop:
```python
for number in range(10):
count = count + number
print('The count is:', count)
```
```error
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-9-dd6a12d7ca5c> in <module>()
1 for number in range(10):
----> 2 count = count + number
3 print('The count is:', count)
NameError: name 'count' is not defined
```
Finally, the third possibility is that you made a typo when you were writing your code.
Let's say we fixed the error above by adding the line `Count = 0` before the for loop.
Frustratingly, this actually does not fix the error.
Remember that variables in Python are [case-sensitive](../learners/reference.md#case-sensitive),
so the variable `count` is different from `Count`. We still get the same error, because we still have not defined `count`:
```python
Count = 0
for number in range(10):
count = count + number
print('The count is:', count)
```
```error
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-10-d77d40059aea> in <module>()
2 for number in range(10):
----> 3 count = count + number
4 print('The count is:', count)
NameError: name 'count' is not defined
```
## Index Errors
Next up are errors having to do with containers (like lists and strings) and the items within them.
If you try to access an item in a list or a string that does not exist,
then you will get an error.
This type of error is called an `IndexError`.
```python
letters = ['a', 'b', 'c']
print('Letter #1 is', letters[0])
print('Letter #2 is', letters[1])
print('Letter #3 is', letters[2])
print('Letter #4 is', letters[3])
```
```error
Letter #1 is a
Letter #2 is b
Letter #3 is c
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-11-d817f55b7d6c> in <module>()
3 print('Letter #2 is', letters[1])
4 print('Letter #3 is', letters[2])
----> 5 print('Letter #4 is', letters[3])
IndexError: list index out of range
```
Here,
Python is telling us that there is an `IndexError` in our code,
meaning we tried to access a list index that did not exist.
## File Errors
The last type of error we'll cover today
are those associated with reading and writing files: `FileNotFoundError`.
If you try to read a file that does not exist,
you will receive a `FileNotFoundError` telling you so.
If you attempt to write to a file that was opened read-only, Python 3
returns an `UnsupportedOperationError`.
More generally, problems with input and output manifest as
`IOError`s or `OSError`s, depending on the version of Python you use.
```python
file_handle = open('myfile.txt', 'r')
```
```error
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-14-f6e1ac4aee96> in <module>()
----> 1 file_handle = open('myfile.txt', 'r')
FileNotFoundError: [Errno 2] No such file or directory: 'myfile.txt'
```
One reason for receiving this error is that you specified an incorrect path to the file.
For example,
if I am currently in a folder called `myproject`,
and I have a file in `myproject/writing/myfile.txt`,
but I try to open `myfile.txt`,
this will fail.
The correct path would be `writing/myfile.txt`.
It is also possible that the file name or its path contains a typo.
A related issue can occur if you use the "read" flag instead of the "write" flag.
Python will not give you an error if you try to open a file for writing
when the file does not exist.
However,
if you meant to open a file for reading,
but accidentally opened it for writing,
and then try to read from it,
you will get an `UnsupportedOperation` error
telling you that the file was not opened for reading:
```python
file_handle = open('myfile.txt', 'w')
file_handle.read()
```
```error
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-15-b846479bc61f> in <module>()
1 file_handle = open('myfile.txt', 'w')
----> 2 file_handle.read()
UnsupportedOperation: not readable
```
These are the most common errors with files,
though many others exist.
If you get an error that you've never seen before,
searching the Internet for that error type
often reveals common reasons why you might get that error.
::: {.callout-note}
## Reading Error Messages
Read the Python code and the resulting traceback below, and answer the following questions:
1. How many levels does the traceback have?
2. What is the function name where the error occurred?
3. On which line number in this function did the error occur?
4. What is the type of error?
5. What is the error message?
```python
# This code has an intentional error. Do not type it directly;
# use it for reference to understand the error message below.
def print_message(day):
messages = {
'monday': 'Hello, world!',
'tuesday': 'Today is Tuesday!',
'wednesday': 'It is the middle of the week.',
'thursday': 'Today is Donnerstag in German!',
'friday': 'Last day of the week!',
'saturday': 'Hooray for the weekend!',
'sunday': 'Aw, the weekend is almost over.'
}
print(messages[day])
def print_friday_message():
print_message('Friday')
print_friday_message()
```
```error
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-1-at line 15, in print_message(day)
13 }
14 print(messages[day])
---> 15
<ipython-input-1-at line 18, in print_friday_message()
17 def print_friday_message():
---> 18 print_message('Friday')
<ipython-input-1-at line 21, in <module>()
20
---> 21 print_friday_message()
KeyError: 'Friday'
```
::: {.callout-tip collapse="true"}
## Solution: Traceback Levels
1. How many levels does the traceback have?
Three levels.
2. What is the function name where the error occurred?
`print_message`
3. On which line number in this function did the error occurred?
Line 14
4. What is the type of error?
`KeyError`
5. What is the error message?
`KeyError: 'Friday'`
:::
:::
::: {.callout-note}
## Identifying Syntax Errors
1. Read the code below, and (without running it) try to identify what the errors are.
2. Run the code, and read the error message. What type of `SyntaxError` do you think this is? Is it a missing colon, a missing quote, a missing parenthesis, etc.? Make note of the line number and column number indicated by the caret (`^`).
3. Fix the error.
4. Repeat steps 2 and 3, until you have fixed all the errors.
```python
def another_function
print('Syntax errors are annoying.')
print('But at least Python tells us about them!')
print('So they are usually not too hard to fix.')
```
::: {.callout-tip collapse="true"}
## Solution: Syntax Errors
The problems with this function are:
1. Missing colon (`:`) at the end of the first line
2. Inconsistent indentation (the third print statement doesn't line up with the others)
```python
def another_function():
print('Syntax errors are annoying.')
print('But at least Python tells us about them!')
print('So they are usually not too hard to fix.')
```
:::
:::
::: {.callout-note}
## Identifying Variable Name Errors
1. Read the code below, and (without running it) try to identify what the errors are.
2. Run the code, and read the error message. What type of `NameError` do you think this is? In other words, is it a string that needs quotes around it, a misspelled variable, or a variable that should have been defined but was not?
3. Fix the error.
4. Repeat steps 2 and 3, until you have fixed all the errors.
```python
for number in range(10):
# use a if the number is a multiple of 3, otherwise use b
if (Number % 3) == 0:
message = message + a
else:
message = message + 'b'
print(message)
```
::: {.callout-tip collapse="true"}
## Solution: Variable Name Errors
3 `NameError`s for `number` being misspelled, for `message` not defined, and for `a` not being in quotes.
Fixed version:
```python
message = ''
for number in range(10):
# use a if the number is a multiple of 3, otherwise use b
if (number % 3) == 0:
message = message + 'a'
else:
message = message + 'b'
print(message)
```
:::
:::
::: {.callout-note}
## Identifying Index Errors
1. Read the code below, and (without running it) try to identify what the errors are.
2. Run the code, and read the error message. What type of error is it?
3. Fix the error.
```python
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print('My favorite season is ', seasons[4])
```
::: {.callout-tip collapse="true"}
## Solution: Index Errors
This list has 4 elements and the index to access the last element is `[3]`.
`seasons[4]` will give an IndexError.
Fixed version:
```python
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print('My favorite season is ', seasons[3])
```
:::
:::
## Key Points
- Tracebacks can look intimidating, but they give us a lot of useful information about what went wrong in our program, including where the error occurred and what type of error it was.
- An error having to do with the 'grammar' or syntax of the program is called a `SyntaxError`. If the issue has to do with how the code is indented, then it will be called an `IndentationError`.
- A `NameError` will occur when trying to use a variable that does not exist. Possible causes are that a variable definition is missing, a variable reference differs from its definition in spelling or capitalization, or the code contains a string that is missing quotes around it.
- Containers like lists and strings will generate errors if you try to access items in them that do not exist. This type of error is called an `IndexError`.