Skip to content

Commit 62ecb7a

Browse files
committed
work on the exceptions
1 parent c338e74 commit 62ecb7a

File tree

1 file changed

+142
-32
lines changed

1 file changed

+142
-32
lines changed

exceptions.md

Lines changed: 142 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,30 @@ exception.
7070

7171
```py
7272
>>> try:
73-
... print(int('123'))
73+
... print(int('lol'))
7474
... except ValueError:
7575
... print("Oops!")
7676
...
77-
123
77+
Oops!
78+
>>>
79+
```
80+
81+
The except part doesn't run if the try part succeeds.
82+
83+
```py
7884
>>> try:
79-
... print(int('lol'))
85+
... print("Hello World!")
8086
... except ValueError:
81-
... print("Oops!")
87+
... print("What the heck? Printing failed!")
8288
...
83-
Oops!
89+
Hello World!
8490
>>>
8591
```
8692

8793
ValueError is raised when something gets an invalid value, but the
8894
value's type is correct. In this case, `int` can take a string as an
89-
argument, but the string needs to contain a number, not `lol`.
90-
91-
If the type is wrong, we will get a TypeError instead.
95+
argument, but the string needs to contain a number, not `lol`. If
96+
the type is wrong, we will get a TypeError instead.
9297

9398
```py
9499
>>> 123 + 'hello'
@@ -98,6 +103,20 @@ TypeError: unsupported operand type(s) for +: 'int' and 'str'
98103
>>>
99104
```
100105

106+
Catching ValueError doesn't catch TypeErrors.
107+
108+
```py
109+
>>> try:
110+
... 123 + 'hello'
111+
... except ValueError:
112+
... print("wrong value")
113+
...
114+
Traceback (most recent call last):
115+
File "<stdin>", line 2, in <module>
116+
TypeError: unsupported operand type(s) for +: 'int' and 'str'
117+
>>>
118+
```
119+
101120
Exceptions always interrupt the code even if we catch them. Here the
102121
print never runs because it's after the error but inside the `try`
103122
block. Everything after the try block runs normally.
@@ -152,6 +171,24 @@ wrong type
152171

153172
Seems to be working.
154173

174+
We can catch multiple exceptions by catching a tuple of exceptions:
175+
176+
```py
177+
>>> try:
178+
... 123 + 'hello'
179+
... except (ValueError, TypeError):
180+
... print('wrong value or type')
181+
...
182+
wrong value or type
183+
>>> try:
184+
... int('lol')
185+
... except (ValueError, TypeError):
186+
... print('wrong value or type')
187+
...
188+
wrong value or type
189+
>>>
190+
```
191+
155192
Catching `Exception` will catch all errors. We'll learn more about why
156193
it does that in a moment.
157194

@@ -203,7 +240,8 @@ There's many things that can go wrong in the `try` block. If something
203240
goes wrong all we have is an oops message that doesn't tell us which
204241
line caused the problem. This makes fixing problems a lot harder. If we
205242
want to catch exceptions we need to be specific about what exactly we
206-
want to catch instead of catching everything we can.
243+
want to catch and where instead of catching everything we can in the
244+
whole program.
207245

208246
There's nothing wrong with doing things like this:
209247

@@ -218,6 +256,8 @@ except OSError: # we can't read the file but we can work without it
218256
Usually catching errors that the user has caused is also a good idea:
219257

220258
```py
259+
import sys
260+
221261
text = input("Enter a number: ")
222262
try:
223263
number = int(text)
@@ -229,53 +269,62 @@ print("Your number doubled is %d." % (number * 2))
229269

230270
## Raising exceptions
231271

232-
Sometimes you may end up doing something like this:
233-
234-
```py
235-
if number < 0:
236-
print("ERROR: number must be non-negative")
237-
```
272+
Now we know how to create exceptions and how to handle errors that
273+
Python creates. But we can also create error messages manually. This
274+
is known as **raising an exception** and **throwing an exception**.
238275

239-
But that's not ideal. If there is an error, the code prints an error
240-
message but it still keeps running. People using your code also don't know
241-
which line in **their** code caused the error.
242-
243-
Instead you can **raise an exception** yourself. Sometimes this is also
244-
called **throwing an exception**.
276+
Raising an exception is easy. All we need to do is to type `raise`
277+
and then an exception we want to raise:
245278

246279
```py
247-
if number < 0:
248-
raise ValueError("number must be non-negative")
280+
>>> raise ValueError("lol is not a number")
281+
Traceback (most recent call last):
282+
File "<stdin>", line 1, in <module>
283+
ValueError: lol is not a number
284+
>>>
249285
```
250286

251-
Let's try that on the interactive prompt, and see what that does.
287+
Of course, we can also raise an exception from a variable.
252288

253289
```py
254-
>>> raise ValueError("number must be non-negative")
290+
>>> oops = ValueError("lol is not a number")
291+
>>> raise oops
255292
Traceback (most recent call last):
256293
File "<stdin>", line 1, in <module>
257-
ValueError: number must be non-negative
294+
ValueError: lol is not a number
258295
>>>
259296
```
260297

261-
Of course, we can also raise an exception from a variable.
298+
If we [define a function](defining-functions.md) that raises an
299+
exception and call it we'll notice that the error message also
300+
says which functions we ran to get to that error.
262301

263302
```py
264-
>>> oops = ValueError("number must be non-negative")
265-
>>> raise oops
303+
>>> def oops():
304+
... raise ValueError("oh no!")
305+
...
306+
>>> def do_the_oops():
307+
... oops()
308+
...
309+
>>> do_the_oops()
266310
Traceback (most recent call last):
267311
File "<stdin>", line 1, in <module>
268-
ValueError: number must be non-negative
312+
File "<stdin>", line 2, in do_the_oops
313+
File "<stdin>", line 2, in oops
314+
ValueError: oh no!
269315
>>>
270316
```
271317

318+
If our code was in a file we would also see the line of code
319+
that raised the error.
320+
272321
## When should we raise exceptions?
273322

274323
Back in [the module chapter](modules.md) we learned to display error
275324
messages by printing to `sys.stderr` and then calling `sys.exit(1)`, so
276325
when should we use that and when should we raise an exception?
277326

278-
Exceptions are meant for **programmers**, so if we're writing a module
327+
Exceptions are meant for **programmers**, so if we are writing a module
279328
that other people will import we should probably use exceptions. For
280329
other errors (for example, if the **user** of the program has done
281330
something wrong) it's usually better to use `sys.stderr` and `sys.exit`.
@@ -348,7 +397,7 @@ older than mine, but they should be mostly similar.
348397
Catching an exception also catches everything that's under it in this
349398
tree. For example, catching `OSError` catches errors that we typically
350399
get when [processing files](files.md), and catching Exception catches
351-
all errors. You don't need to remember this tree, running
400+
all of these errors. You don't need to remember this tree, running
352401
`help('builtins')` should display a larger tree that this is a part of.
353402

354403
## Summary
@@ -362,3 +411,64 @@ all errors. You don't need to remember this tree, running
362411
is also known as throwing exceptions.
363412
- Raise exceptions if they are meant to be displayed for programmers and
364413
use `sys.stderr` and `sys.exit` otherwise.
414+
415+
## Examples
416+
417+
Keep asking a number from the user until it's entered correctly.
418+
419+
```py
420+
while True:
421+
try:
422+
number = int(input("Enter a number: "))
423+
break
424+
except ValueError:
425+
print("That's not a valid number! Try again.")
426+
427+
print("Your number doubled is:", number * 2)
428+
```
429+
430+
This program allows the user to customize the message it prints by
431+
modifying a file the greeting is stored in, and it can create the
432+
file for the user if it doesn't exist already. This example also uses
433+
things from [the file chapter](files.md), [the function defining
434+
chapter](defining-functions.md) and [the module chapter](modules.md).
435+
436+
```py
437+
# These are here so you can change them to customize the program
438+
# easily.
439+
default_greeting = "Hello World!"
440+
filename = "greeting.txt"
441+
442+
443+
import sys
444+
445+
def askyesno(question):
446+
while True:
447+
answer = input(question + ' (y or n) ')
448+
if answer == 'Y' or answer == 'y':
449+
return True
450+
if answer == 'N' or answer == 'n':
451+
return False
452+
453+
def greet():
454+
with open(filename, 'r') as f:
455+
for line in f:
456+
print(line.rstrip('\n'))
457+
458+
try:
459+
greet()
460+
except OSError:
461+
print("Cannot read '%s'!" % filename, file=sys.stderr)
462+
if askyesno("Would you like to create a default greeting file?"):
463+
try:
464+
with open(filename, 'w') as f:
465+
print(default_greeting, file=f)
466+
except OSError:
467+
print("Cannot create '%s' :(" % filename, file=sys.stderr)
468+
sys.exit(1) # time to give up
469+
greet()
470+
```
471+
472+
***
473+
474+
You may use this tutorial at your own risk. See [LICENSE](LICENSE).

0 commit comments

Comments
 (0)