Discussion:
[Tutor] Terminology question
Jim Mooney Py3.4.3winXP
2015-05-14 22:43:30 UTC
Permalink
I noticed that if I call a function that throws an error, I can catch it
from the caller, instead of catching it in the function. Is this is what is
known as "errors bubbling up?" Also, is this how you're supposed to do it?

*** Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600
32 bit (Intel)] on win32. ***


'''Print a table of random numbers, given min, max, rows, and columns'''
from random import randint


def make_table(minimum, maximum, rows, columns):
for row in range(rows):
print()
for column in range(columns):
print(randint(minimum, maximum), end = ' ')


def get_input():
inp = input("Enter minimum, maximum, rows, and columns, separated by
commas: ")
inps = inp.split(',')

try:
minimum = int(inps[0])
maximum = int(inps[1])
rows = int(inps[2])
columns = int(inps[3])
return minimum, maximum, rows, columns
except ValueError as err:
print("non-integer entered", err)
return None
except IndexError as err:
print("You didn't enter enough values.", err)
return None

vals = get_input()
if vals:
minimum, maximum, rows, columns = vals
try:
make_table(minimum, maximum, rows, columns)
except ValueError as err: # CATCH FUNCTION ERROR HERE INSTEAD OF IN
FUNCTION
print("Enter min before max.")
else:
print('Nothing to do.')
--
Jim
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-05-14 23:47:30 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
I noticed that if I call a function that throws an error, I can catch it
from the caller, instead of catching it in the function. Is this is what is
known as "errors bubbling up?"
Yes. They can bubble up naturally because there are no handlers lower
down or you can partially handle it in the function (eg by adding some
error data to the exception) then call raise to push it up to the next
level.
Post by Jim Mooney Py3.4.3winXP
Also, is this how you're supposed to do it?
More or less, except you should aim to deal with errors at the earliest
opportunity. But bvery often you don;t know the best way to deal with an
error at the point where it occurs, you need a wider context. So in that
case bubbling up to a higher level, with a view of the context is the
right thing to do.
...
Post by Jim Mooney Py3.4.3winXP
minimum = int(inps[0])
maximum = int(inps[1])
rows = int(inps[2])
columns = int(inps[3])
return minimum, maximum, rows, columns
print("non-integer entered", err)
return None
print("You didn't enter enough values.", err)
return None
Rather than printing the messages you could re-raise
the error but with the error message attached as a string.
Or, more usefully in a function called get_input() force
it to go round a while loop until they provide valid
input.
Post by Jim Mooney Py3.4.3winXP
vals = get_input()
minimum, maximum, rows, columns = vals
make_table(minimum, maximum, rows, columns)
make_table(*vals)

would be neater

But it would be better to catch bad input data in the get_input()
function rather than wait till you use it. It will make debugging easier
and potentially improve the reliability of the get_input()
in terms of reuse opportunities. After all it's the get_input()
function's job to get input, so it should check that the inputs
are sane.

So for example you could do some basic checks like ensuring
min <= max, and that rows and cols are non-zero. If not then
either get more inputs or throw a ValueError at that point.
Your try/except then goes round get_input() not the function
that uses the inputs.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Jim Mooney Py3.4.3winXP
2015-05-15 19:02:44 UTC
Permalink
Post by Alan Gauld
Rather than printing the messages you could re-raise
the error but with the error message attached as a string.
I'm a little unclear how you catch the string you create in a raise, in the
caller. I tried an example from the docs but it didn't work. Could you
provide a simple example? Sometimes the docs are heavy slogging if you
don't already know what's what ;')
--
Jim
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Mark Lawrence
2015-05-15 19:54:27 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
Post by Alan Gauld
Rather than printing the messages you could re-raise
the error but with the error message attached as a string.
I'm a little unclear how you catch the string you create in a raise, in the
caller. I tried an example from the docs but it didn't work. Could you
provide a simple example? Sometimes the docs are heavy slogging if you
don't already know what's what ;')
Please help us to help you, if you were to show the example from the
docs that "didn't work" we could easily clarify the situation for you.
--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-05-16 05:45:17 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
Post by Alan Gauld
Rather than printing the messages you could re-raise
the error but with the error message attached as a string.
I'm a little unclear how you catch the string you create in a raise, in the
caller. I tried an example from the docs but it didn't work.
What does "didn't work" mean? Did your computer crash? Catch fire? A
completely different error got printed? Something else?
Post by Jim Mooney Py3.4.3winXP
Could you
provide a simple example? Sometimes the docs are heavy slogging if you
don't already know what's what ;')
I'm not entirely sure what part you are having trouble with. I assume
you know how to catch an exception:

alist = []
try:
value = alist[2]
except IndexError:
pass # Just pretend it didn't happen...


and how to inspect its error message:



alist = []
try:
value = alist[2]
except IndexError as err:
print(err.args[0])



We can also *replace* the error message with one of our own. The args
attribute is a tuple, normally the error message is in the zeroeth
position as above. So we just have to replace args with a tuple of our
own and re-raise the exception:


try:
value = alist[2]
except IndexError as err:
err.args = ("Er, wot?",) + err.args[1:]
raise



An alternative is to replace the exception with a completely different
exception:



try:
value = alist[2]
except IndexError:
raise RuntimeError("er, wot?")



In Python 2, this suppresses the IndexError and raises RuntimeError
instead. However, this hides bugs in the case that the second exception
isn't deliberate, but a bug. So starting with Python 3, exceptions are
chained, which means that if an exception occurs inside an "except"
block, Python will display both exceptions and not just the most recent:


Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: list index out of range

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: er, wot?



This is a great boon for debugging accidental bugs, but doesn't help
much if you actually do intend to replace one exception with another. So
starting from Python 3.3 you can suppress the chaining:

try:
value = alist[2]
except IndexError:
raise RuntimeError("er, wot?") from None



which gives just a single exception:

Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: er, wot?



Does this help?
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Jim Mooney Py3.4.3winXP
2015-05-16 16:56:33 UTC
Permalink
Post by Steven D'Aprano
What does "didn't work" mean? Did your computer crash? Catch fire? A
completely different error got printed? Something else?
I can see I was marvelously unclear ;') Here is what I meant.

def make_error():
raise ZeroDivisionError('Here I am')

def call_error():
try:
make_error()
except:
print("How do I get the 'Here I am' message to print in the calling
routine?")
Post by Steven D'Aprano
call_error()
How do I get the 'Here I am' message to print in the calling routine?
--
Jim

"What a rotten, failed experiment. I'll start over. Maybe dogs instead of
monkeys this time." --God
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Alan Gauld
2015-05-16 17:12:52 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
I can see I was marvelously unclear ;') Here is what I meant.
raise ZeroDivisionError('Here I am')
make_error()
print("How do I get the 'Here I am' message to print in the calling
routine?")
Post by Jim Mooney Py3.4.3winXP
call_error()
How do I get the 'Here I am' message to print in the calling routine?
And the remainder of Steven's reply showed you how, using the
args attribute of the error.
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Steven D'Aprano
2015-05-16 17:57:16 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
raise ZeroDivisionError('Here I am')
make_error()
print("How do I get the 'Here I am' message to print in the calling
routine?")
Okay, here is a hint for you:

Every time you use a bare "except", god kills a puppy. Please, won't you
think of the poor little puppies?

All joking aside, using a bare except is *nearly always* not what you
want, and poor practice. The problem is that it catches too much, and so
masks bugs that should be fixed. You should only catch exceptions that
you know about and that you can recover from. Everything else is an
indication that your code has a bug that needs fixing.

But, having said that, sometimes I'm lazy too, and when experimenting at
the interactive interpreter, I just write "except:" without specifying a
specific exception. Just don't let it become a habit in real code.
Post by Jim Mooney Py3.4.3winXP
Post by Jim Mooney Py3.4.3winXP
call_error()
How do I get the 'Here I am' message to print in the calling routine?
The easiest and best way is to just *not* catch the exception and let
Python print a traceback. But if you want to look at the error message
while recovering from the exception, you have to catch and name the
exception. You can specify multiple types by using a tuple of exception
types:

try:
...
except (TypeError, ValueError) as err:
print(err.args[0])


Notice that there is no supported syntax for leaving the exception type
blank while still naming it.

Remember that exceptions are objects too, and the various exception
types are classes. You can subclass them to make your own types:

class MyValueError(ValueError):
pass

raise MyValueError


Also, all exceptions inherit from a single class: BaseException. So you
can say:

except BaseException as error

which is (nearly[1]) the same as a bare except clause, but giving it a
name. Normally, however, that's not what you want, since that will also
catch things like the user typing Ctrl-C to interrupt a infinite loop,
and other exceptions which don't represent errors.

To just catch errors, use "except Exception as error", then inspect
error.args[0].




[1] There is one slight difference. Up to Python 2.5, you could raise
and catch strings as well as exceptions. This turned out to be a bad
idea, and was finally removed in 2.6. But if you are running 2.5 or
older, a bare except clause is the only way to catch string exceptions
unless you know exactly what string it it.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Ben Finney
2015-05-15 00:00:06 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
I noticed that if I call a function that throws an error
(Known in Python as “raise an exception”. I understood you, but it's
better to use the terminology that matches what the Python docs say.)
Post by Jim Mooney Py3.4.3winXP
I can catch it from the caller, instead of catching it in the
function. Is this is what is known as "errors bubbling up?" Also, is
this how you're supposed to do it?
Yes, the “call stack” (the stack of function calls waiting for execution
to return to them) can be thought of as a vertical stack through which
an exception will “bubble up”.

You should allow exceptions to bubble up to a point where they can be
handled sensibly.

Conversely, you should only ever catch a (type of) excption that you
will be able to handle sensibly at that point, otherwise allow it to
continue up the stack.
--
\ “It's all in the mind, you know.” —The Goon Show |
`\ |
_o__) |
Ben Finney

_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://
Steven D'Aprano
2015-05-16 06:04:17 UTC
Permalink
Post by Jim Mooney Py3.4.3winXP
I noticed that if I call a function that throws an error, I can catch it
from the caller, instead of catching it in the function. Is this is what is
known as "errors bubbling up?" Also, is this how you're supposed to do it?
Consider a chain of functions:

main() calls setup();
setup() calls process_arguments();
process_arguments() calls read_config();
read_config() calls open("somefile");

and open("somefile") raises an exception (perhaps the file cannot be
found, or you don't have permission to read it).


Each function in the chain gets a chance to catch the exception:

open <- read_config <- process_arguments <- setup <- main


If open doesn't catch the exception, we say that the exception "bubbles
up" the chain until it is caught, or if none of them catch it, then it
bubbles all the way up and finally reaches the Python interpreter, which
prints a stack trace and exits.


[...]
Post by Jim Mooney Py3.4.3winXP
inp = input("Enter minimum, maximum, rows, and columns, separated by
commas: ")
inps = inp.split(',')
minimum = int(inps[0])
maximum = int(inps[1])
rows = int(inps[2])
columns = int(inps[3])
return minimum, maximum, rows, columns
This is not best practice. There are too many things which might cause
an exception. Ideally, a try block should only contain *one* thing which
might go wrong. Here you have at least four. By treating them all the
same, you lose the opportunity to give the user information about
*which* one they screwed up.
Post by Jim Mooney Py3.4.3winXP
print("non-integer entered", err)
return None
This is bad practice, for two reasons.

You might not think so yet, but Python tracebacks give you a lot of
useful debugging information. Here, you flush that useful information
down the toilet and replace it with a not-very helpful message
"non-integer entered" (plus a bit more stuff). In a trivial script that
might not matter, but in a big script, you may have *no idea* where this
non-integer was entered. There could be thirty places where it might
happen. How do you know which one it is?

The second reason is that you really should only catch exceptions that
you can do something about. If you can't fix the problem, there's no
point in catching the exception[1]. In this case, if the user doesn't
enter a valid set of values, there's no way you can proceed. So why
bother delaying the inevitable? Just let the exception exit the program.

This will also look after all the little things which distinguish a
professional quality application from a jerry-built beginner's toy. For
example: error messages should print to stderr, not stdout; the
application should exit with a non-zero result code. If this means
nothing to you, don't be too concerned about it, just understand that
Python will do the right thing provided you don't get in its way, as you
just did by catching the exception.





[1] Well, there is one other reason: in an application aimed at
non-programmers, you might choose to catch the exception, log the
traceback somewhere where you can get to it for debugging, and display a
"friendly" (i.e. non-technical, non-threatening, useless) message to the
user. But as a beginner yourself, you shouldn't do this until you have
become an expert in reading tracebacks. Otherwise you're just hiding
debugging info that you need.
--
Steve
_______________________________________________
Tutor maillist - ***@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor
Continue reading on narkive:
Loading...