How to name Exceptions that aren't Errors

L

Leo Breebaart

I've recently become rather fond of using Exceptions in Python to
signal special conditions that aren't errors, but which I feel
are better communicated up the call stack via the exception
mechanism than via e.g. return values.

For instance, I'm thinking of methods such as:


def run(self):
""" Feed the input file to the simulator. """

for linenr, line in enumerate(self.infile):
try:
current_values = self.parse_line(linenr, line)
==> except CommentLineException:
continue
results = self.do_one_simulation_step(current_values)
self.process_simulation_results(results)


which I use in order to discard comments from a file I'm parsing
line-by-line.

My question is twofold. First, I know that many programmers are
violently opposed to using exceptions in this fashion, i.e. for
anything other than, well, exceptional circumstances. But am I
correct in thinking that in Python this usage is actually
considered quite normal, and not frowned upon? Is my snippet
above indeed sufficiently Pythonic?

Second, purely as a question of aesthetics, I was wondering if
the folks here might have any opinions about actual naming
conventions for the exception classes used in this fashion.

'CommentLineError' would clearly be wrong, but using the
'Exception' prefix seems a bit redundant and pointless too. I
suppose I could just call the exception "CommentLine" and leave
it at that, but I don't know, maybe there's something better I'm
overlooking.

Any suggestions?
 
S

Steve Holden

Leo said:
I've recently become rather fond of using Exceptions in Python to
signal special conditions that aren't errors, but which I feel
are better communicated up the call stack via the exception
mechanism than via e.g. return values.
Absolutely.

For instance, I'm thinking of methods such as:


def run(self):
""" Feed the input file to the simulator. """

for linenr, line in enumerate(self.infile):
try:
current_values = self.parse_line(linenr, line)
==> except CommentLineException:
continue
results = self.do_one_simulation_step(current_values)
self.process_simulation_results(results)


which I use in order to discard comments from a file I'm parsing
line-by-line.
This specific example assumes that it isn't possible to easily determine
by examination that the line is a comment, otherwise it's more readably
re-cast as

for linenr, line in enumerate(self.infile):
if not isComment(line):
current_values = self.parse_line(linenr, line)
results = self.do_one_simulation_step(current_values)
self.process_simulation_results(results)

but the general point is still a valid one, so I'll assume you just
grabbed something that was readily to hand.
My question is twofold. First, I know that many programmers are
violently opposed to using exceptions in this fashion, i.e. for
anything other than, well, exceptional circumstances. But am I
correct in thinking that in Python this usage is actually
considered quite normal, and not frowned upon? Is my snippet
above indeed sufficiently Pythonic?
Well, you will doubtless get as many opinions as you consult
programmers, but in general there's much more tolerance in the Python
world for such programming methods, and indeed much more tolerance
generally than in some communities I've been a part of.
Second, purely as a question of aesthetics, I was wondering if
the folks here might have any opinions about actual naming
conventions for the exception classes used in this fashion.

'CommentLineError' would clearly be wrong, but using the
'Exception' prefix seems a bit redundant and pointless too. I
suppose I could just call the exception "CommentLine" and leave
it at that, but I don't know, maybe there's something better I'm
overlooking.
Here you could be guided by the standard hierarchy, quoted here from the
2.4 documentation:

Exception
+-- SystemExit
+-- StopIteration
+-- StandardError
| +-- KeyboardInterrupt
| +-- ImportError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError
| +-- EOFError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- NameError
| | +-- UnboundLocalError
| +-- AttributeError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- TypeError
| +-- AssertionError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- ArithmeticError
| | +-- OverflowError
| | +-- ZeroDivisionError
| | +-- FloatingPointError
| +-- ValueError
| | +-- UnicodeError
| | +-- UnicodeEncodeError
| | +-- UnicodeDecodeError
| | +-- UnicodeTranslateError
| +-- ReferenceError
| +-- SystemError
| +-- MemoryError
+---Warning
+-- UserWarning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- SyntaxWarning
+-- OverflowWarning (not generated in 2.4; won't exist in 2.5)
+-- RuntimeWarning
+-- FutureWarning
Any suggestions?
Obviously *Error and *Warning predominate, but I would suggest it's
largely a matter of code readability. I've even used an exception called
Continue to overcome an irksome restriction in the language (you used
not to be able to continue a loop from an except clause).

As long as the intent of the code is obvious to the casual reader I
suspect it's very unlikely you'll get complaints.

except CommentLine:
pass

seems reasonably comprehensible, so time spent arguing about it would be
better devoted to a discussion of the number of angels that could dance
on the head of a pin.

regards
Steve
 
F

F. Petitjean

Le 7 Apr 2005 19:23:21 GMT, Leo Breebaart a écrit :
I've recently become rather fond of using Exceptions in Python to
signal special conditions that aren't errors, but which I feel
are better communicated up the call stack via the exception
mechanism than via e.g. return values.

For instance, I'm thinking of methods such as:


def run(self):
""" Feed the input file to the simulator. """

for linenr, line in enumerate(self.infile):
try:
current_values = self.parse_line(linenr, line)
==> except CommentLineException:
continue
results = self.do_one_simulation_step(current_values)
self.process_simulation_results(results)


which I use in order to discard comments from a file I'm parsing
line-by-line.
[snip]
'Exception' prefix seems a bit redundant and pointless too. I
suppose I could just call the exception "CommentLine" and leave
it at that, but I don't know, maybe there's something better I'm
overlooking.
You are overlooking the fact the flow of information is pretty much
linear. The comments lines can be safely ignored (filtered out) on the
fly.
enumerate filter-out
infile(producer) --------> linenr, line -----------> linenr, line --->
Any suggestions?
From your parse_line() method extract the logic to detect a comment line
put this code in a predicate function and use itertools.ifilter(pred,
iterable). Much more explicit. parse_line() is simplified. The client
code run() does not have to deal with bogus inputs anymore if you feed
it with the filtered out stream.
 
M

Max

Leo said:
I've recently become rather fond of using Exceptions in Python to
signal special conditions that aren't errors, but which I feel
are better communicated up the call stack via the exception
mechanism than via e.g. return values.

Ummm... yeah, I quite agree.

LOOK EVERYONE, it's Leo Breebart. This guys famous in the alternative
universe of alt.fan.pratchett.

You are the same Leo Breebart, right?

Well done, APF9 is excellent. But what did we expect.

--Max
 
A

Aahz

My question is twofold. First, I know that many programmers are
violently opposed to using exceptions in this fashion, i.e. for
anything other than, well, exceptional circumstances. But am I correct
in thinking that in Python this usage is actually considered quite
normal, and not frowned upon? Is my snippet above indeed sufficiently
Pythonic?

Consider the use of StopIteration for ``for`` loops, and you will be
Enlightened.
--
Aahz ([email protected]) <*> http://www.pythoncraft.com/

"The joy of coding Python should be in seeing short, concise, readable
classes that express a lot of action in a small amount of clear code --
not in reams of trivial code that bores the reader to death." --GvR
 
L

Leo Breebaart

Max said:
LOOK EVERYONE, it's Leo Breebart. You are the same Leo
Breebart, right?

Breeb*aa*rt. But otherwise, yeah -- I do frequent more than just
one newsgroup. :)

This guys famous in the alternative universe of
alt.fan.pratchett.

I doubt anybody here cares! Who was it that said: "On the
Internet, everyone's famous to fifteen other people"...?

Anyways, regardless of any feeble claims to notoriety I may have
in alternate universes, here in Python country I am but a humble
grasshopper wot has only been programming in Python for slightly
over a year now.

*Dutch* grasshopper, though. Does that help any?
 
B

Bengt Richter

It also possible for exception arguments to deliver a result, rather
than indicate something rejected. E.g., you can terminate a recursive
search via an exception. If no exception occurs, you have gone through
the entire search space and not met the solution criterion.

Regards,
Bengt Richter
 
R

Roy Smith

Leo Breebaart said:
I've recently become rather fond of using Exceptions in Python to
signal special conditions that aren't errors, but which I feel
are better communicated up the call stack via the exception
mechanism than via e.g. return values.

For instance, I'm thinking of methods such as:


def run(self):
""" Feed the input file to the simulator. """

for linenr, line in enumerate(self.infile):
try:
current_values = self.parse_line(linenr, line)
==> except CommentLineException:
continue
results = self.do_one_simulation_step(current_values)
self.process_simulation_results(results)

I know this isn't the question you asked, but I would have written a little
generator function which hides the comment processing in a lower level:

def nonCommentLines (file):
for lineNumber, line in enumerate (file):
if not line.startswith('#'):
yield lineNumber, line

for lineNumber, line in nonCommentLines (sys.stdin):
current_values = self.parse_line(linenr, line)
results = self.do_one_simulation_step(current_values)
self.process_simulation_results(results)

This assumes you don't mind your line numbers starting from zero, but your
version has the same behavior.
My question is twofold. First, I know that many programmers are
violently opposed to using exceptions in this fashion, i.e. for
anything other than, well, exceptional circumstances. But am I
correct in thinking that in Python this usage is actually
considered quite normal, and not frowned upon? Is my snippet
above indeed sufficiently Pythonic?

I think my code is clearer, but I wouldn't go so far as to say I'm
violently opposed to your code. I save violent opposition for really
important matters like which text editor you use.

I suspect if you showed your code to a C++ guy, he might be violently
opposed. In C++, exceptions are perceived to be more heavyweight than they
are in Python. Some of this is just perception (perhaps colored by
historical issues with older C++ compilers not handling exceptions well),
some of it is real. Just the other day, a colleague of mine was telling me
about a bug in his code. It happened because he had failed to write a copy
constructor for an exception class he wrote. The mind boggles how anybody
can get anything useful done in a language like that. But I digress.

In Python, exceptions seem to be the much more accepted way of doing
things. You often see:

try:
value = dictionary[key]
except KeyError:
whatever

instead of:

if dictionary.has_key (key):
value = dictionary[key]
else:
whatever

While in C++, the "if" version would be far more common.
Second, purely as a question of aesthetics, I was wondering if
the folks here might have any opinions about actual naming
conventions for the exception classes used in this fashion.

If you look at the list of standard exceptions (import exceptions and do a
dir() on it), you'll see things like KeyboardInterrupt, StopIteration, and
SystemExit. If the name fits, raise it.
 
S

Steve Holden

Roy Smith wrote:
[...]
I think my code is clearer, but I wouldn't go so far as to say I'm
violently opposed to your code. I save violent opposition for really
important matters like which text editor you use.

+1 QOTW

regards
Steve
 
L

Leif K-Brooks

Steve said:
I've even used an exception called Continue to overcome an irksome
restriction in the language (you used not to be able to continue a
loop from an except clause).

Out of curiosity, how could you use an exception to do that? I would
think you would need to catch it and then use "continue", which wouldn't
be possible because of the restriction you were trying to work around in
the first place.
 
S

Scott David Daniels

Leif said:
Out of curiosity, how could you use an exception to do that? I would
think you would need to catch it and then use "continue", which wouldn't
be possible because of the restriction you were trying to work around in
the first place.

Here is a stupid way to calculate a 3-element median:

class Success(Exception): pass

def median_if_sorted(x,y,z):
if x >= y >= z:
raise Success(y)
def median(a, b, c):
try:
median_if_sorted(a, b, c)
median_if_sorted(c, b, a)
median_if_sorted(a, c, b)
median_if_sorted(c, a, b)
median_if_sorted(b, a, c)
median_if_sorted(b, c, a)
except Success, instance:
result, = instance.args
return result
else:
raise ValueError, 'Nothing found'

--Scott David Daniels
(e-mail address removed)
 
S

Steve Holden

Leif said:
Out of curiosity, how could you use an exception to do that? I would
think you would need to catch it and then use "continue", which wouldn't
be possible because of the restriction you were trying to work around in
the first place.

As long as the exception-handling code doesn't break then the loop
automatically continues, so the trick was (IIRC) to have a loop body
that essentially looked like this:

for item in somelist:
try:
{loop body}
except Continue:
pass

Then exceptions caught inside the loop body (which obviously had nested
try: clauses) could raise Continue inside their except: clause to
restart the loop at the next iteration.

regards
Steve
 
C

Christos TZOTZIOY Georgiou

On 7 Apr 2005 21:20:19 GMT, rumours say that Leo Breebaart
<[email protected]> might have written:

[Max about Leo]
The little imp stopped moving the memory blocks around as soon as it
heard the distinct click of the name "Breebart"[1] falling into place.


[1] "Breeb*aa*rt" you say. Ok then, double click.

I doubt anybody here cares!

I WAS EXPECTING TO MEET THEE IN ALT.FAN.PRATCHETT.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top