Proper way to express application-specific exceptions - language-agnostic

I'm trying to find a good way to express exceptions in dynamic-typed languages (e.g. Python, although the same code can be used with e.g. enums in static-typed languages). In my application, the exception is not going to be displayed to the user. Which one would be best? (or you can propose better)
def parseData(data):
length = unpack('!L', data[0:4])
if 4 + len(data) != length:
Option 1:
raise Exception("Invalid length")
Option 2:
return -1
# Some code later...
parseResult = parseData(data)
if validationResult == -1:
# Do something with the error.
The point is that when user doesn't see the exception, is it worth the hassle of making custom exception types instead of coming the easy path and returning integer values? (this is often done in functions like .indexOf(...)).

I can only comment on Python, but I would only extremely rarely raise Exception, as it makes error handling much harder; except Exception would catch many legitimate errors I would much rather hear about.
Instead, I would raise something more meaningful from the built-in exceptions:
raise ValueError("Invalid length.")
Most of Python's built-in classes and functions would raise an exception rather than returning some flag value. The only exception I can immediately think of is str.find, which will return -1 if the sub-string can't be found (its partner str.index will raise ValueError; I find this preferable, as -1 is a valid index).
There may be the occasional case where a single function could raise one of a range of exceptions, depending on what exactly has happened, but this is unusual - if your response will depend on what went wrong, that logic should probably be inside the function. You can still stick to the built-ins, for example:
def divide(n, lst, index):
return n / lst[index]
could raise TypeError, IndexError or ZeroDivisionError, and I can deal with that accordingly:
try:
divide(1, {3: 4, 5: 6}, 2)
except TypeError:
...
except IndexError:
...
except ZeroDivisionError:
...
except Exception as e:
print("Didn't see that coming: {0}".format(repr(e)))
You can even inspect the message in the exception to differentiate, if necessary:
def test():
test()
try:
test()
except RuntimeError as e:
if "recursion" in e.args[0]
print("Ran out of stack.")
else:
print("That was unexpected: {0}".format(repr(e)))

Related

Python - pythoncom.com_error handling in Python 3.2.2

I am using Python 3.2.2, and building a Tkinter interface to do some Active Directory updating. I am having trouble trying to handle pythoncom.com_error exceptions.
I grabbed some code from here:
http://code.activestate.com/recipes/303345-create-an-account-in-ms-active-directory/
However, I use the following (straight from the above site) handle the exceptions raised:
except pythoncom.com_error,(hr,msg,exc,arg):
This code is consistent with many of the sites I have seen that handle these exceptions, however with Python 3.2.2, I get a syntax error if I include the comma after "pythoncom.com_error". If I remove the comma, the program starts, but then when the exception is raised, I get other exceptions because "hr", "msg" etc are not defined as global variables.
If I remove the comma and all of the bits in the brackets, then it all works well, except I can't see exactly what happens in the exception, which I want so I can pass through the actual error message from AD.
Does anyone know how to handle these pythoncom exceptions properly in Python 3.2.2?
Thanks in advance!
You simply need to use the modern except-as syntax, I think:
import pythoncom
import win32com
import win32com.client
location = 'fred'
try:
ad_obj=win32com.client.GetObject(location)
except pythoncom.com_error as error:
print (error)
print (vars(error))
print (error.args)
hr,msg,exc,arg = error.args
which produces
(-2147221020, 'Invalid syntax', None, None)
{'excepinfo': None, 'hresult': -2147221020, 'strerror': 'Invalid syntax', 'argerror': None}
(-2147221020, 'Invalid syntax', None, None)
for me [although I'm never sure whether the args order is really what it looks like, so I'd probably refer to the keys explicitly; someone else may know for sure.]
I use this structure (Python 3.5) --
try:
...
except Exception as e:
print ("error in level argument", e)
...
else:
...

Python practices: Is there a better way to check constructor parameters?

I find myself trying to convert constructor parameters to their right types very often in my Python programs. So far I've been using code similar to this, so I don't have to repeat the exception arguments:
class ClassWithThreads(object):
def __init__(self, num_threads):
try:
self.num_threads= int(num_threads)
if self.num_threads <= 0:
raise ValueError()
except ValueError:
raise ValueError("invalid thread count")
Is this a good practice? Should I just don't bother catching any exceptions on conversion and let them propagate to the caller, with the possible disadvantage of having less meaningful and consistent error messages?
When I have a question like this, I go hunting in the standard library for code that I can model my code after. multiprocessing/pool.py has a class somewhat close to yours:
class Pool(object):
def __init__(self, processes=None, initializer=None, initargs=(),
maxtasksperchild=None):
...
if processes is None:
try:
processes = cpu_count()
except NotImplementedError:
processes = 1
if processes < 1:
raise ValueError("Number of processes must be at least 1")
if initializer is not None and not hasattr(initializer, '__call__'):
raise TypeError('initializer must be a callable')
Notice that it does not say
processes = int(processes)
It just assumes you sent it an integer, not a float or a string, or whatever.
It should be pretty obvious, but if you feel it is not, I think it suffices to just document it.
It does raise ValueError if processes < 1, and it does check that initializer, when given, is callable.
So, if we take multiprocessing.Pool as a model, your class should look like this:
class ClassWithThreads(object):
def __init__(self, num_threads):
self.num_threads = num_threads
if self.num_threads < 1:
raise ValueError('Number of threads must be at least 1')
Wouldn't this approach possibly fail very unpredictably for some
conditions?
I think preemptive type checking generally goes against the grain of Python's
(dynamic-, duck-typing) design philosophy.
Duck typing gives Python programmers opportunities for great expressive power,
and rapid code development but (some might say) is dangerous because it makes no
attempt to catch type errors.
Some argue that logical errors are far more serious and frequent than type
errors. You need unit tests to catch those more serious errors. So even if you
do do preemptive type checking, it does not add much protection.
This debate lies in the realm of opinions, not facts, so it is not a resolvable argument. On which side of the fence
you sit may depend on your experience, your judgment on the likelihood of type
errors. It may be biased by what languages you already know. It may depend on
your problem domain.
You just have to decide for yourself.
PS. In a statically typed language, the type checks can be done at compile-time, thus not impeding the speed of the program. In Python, the type checks have to occur at run-time. This will slow the program down a bit, and maybe a lot if the checking occurs in a loop. As the program grows, so will the number of type checks. And unfortunately, many of those checks may be redundant. So if you really believe you need type checking, you probably should be using a statically-typed language.
PPS. There are decorators for type checking for (Python 2) and (Python 3). This would separate the type checking code from the rest of the function, and allow you to more easily turn off type checking in the future if you so choose.
You could use a type checking decorator like this activestate recipe or this other one for python 3. They allow you to write code something like this:
#require("x", int, float)
#require("y", float)
def foo(x, y):
return x+y
that will raise an exception if the arguments are not of the required type. You could easily extend the decorators to check that the arguments have valid values aswell.
This is subjective, but here's a counter-argument:
>>> obj = ClassWithThreads("potato")
ValueError: invalid thread count
Wait, what? That should be a TypeError. I would do this:
if not isinstance(num_threads, int):
raise TypeError("num_threads must be an integer")
if num_threads <= 0:
raise ValueError("num_threads must be positive")
Okay, so this violates "duck typing" principles. But I wouldn't use duck typing for primitive objects like int.

How to execute something if any exception happens

A python newbie question: I need to do the following
try:
do-something()
except error1:
...
except error2:
...
except:
...
#Here I need to do something if any exception of the above exception was thrown.
I can set a flag and do this. But is there a cleaner way of doing this?
Actually I don't like flags and consider them as the last resort solution. In this case I'd consider something like this:
def f():
try:
do_something()
except E1:
handle_E1()
except E2:
handle_E2()
else:
return
do_stuff_to_be_done_in_case_any_exception_occurred()
Of course, this is only an option if you can return in the else: case.
Another option might be to rethrow the exception and recatch it for a more general handling of errors. This might even be the cleanest approach:
def f():
try: # general error handling
try: # specific error handling
do_something()
except E1:
handle_E1()
raise
except E2:
handle_E2()
raise
except (E1, E2):
do_stuff_to_be_done_in_case_any_exception_occurred()
I just tried a couple different idea's out and it looks like a flag is your best bet.
else suite is only called if there is no exception
finally will always be called
You can do this with a nested try. The except block of the outer try should catch all exceptions. Its body is another try that immediately re-raises the exception. The except blocks of the inner try actually handle the individual exceptions. You can use the finally block in the inner try to do what you want: run something after any exception, but only after an exception.
Here is a little interactive example (modeled on Applesoft BASIC for nostalgia purposes).
try:
input("]") # for Python 3: eval(input("]"))
except:
try:
#Here do something if any exception was thrown.
raise
except SyntaxError:
print "?SYNTAX",
except ValueError:
print "?ILLEGAL QUANTITY",
# additional handlers here
except:
print "?UNKNOWN",
finally:
print "ERROR"
This is the best way I can think of. Looks like a code smell though
try:
exception_flag = True
do-something()
exception_flag = False
except error1:
...
except error2:
...
except:
...
finally:
if exception_flag:
...
You wouldn't need the finally if you are not reraising exceptions in the handler
From the docs: http://docs.python.org/reference/compound_stmts.html#finally
If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception, it is re-raised at the end of the finally clause. If the finally clause raises another exception or executes a return or break statement, the saved exception is lost. The exception information is not available to the program during execution of the finally clause.
It's not clear if you need to handle differently error1, error2 etc. If not, then the following code will do the trick:
try:
do_something()
except (error1, error2, error3), exception_variable:
handle_any_of_these_exceptions()
if you do need to handle different errors differently as well as having the common code, then in the except block, you can have code of the following type:
if isinstance(exception_variable, error1):
do_things_specific_to_error1()
I think this is a more neat solution by using return in the try clause.
If everything works, we will return the value we got in bar(). If we get an exception, we will run the next code, in this case raising another exception.
Demonstrated with a randint.
import random
def foo():
try:
return bar()
except IndexError:
print('Error.')
raise KeyError('Error msg')
def bar():
res = random.randint(0, 2)
if res == 0:
raise IndexError
return res
res = foo()
print(res)

Elegant way to handle "impossible" code paths

Occasionally I'll have a situation where I've written some code and, based on its logic, a certain path is impossible. For example:
activeGames = [10, 20, 30]
limit = 4
def getBestActiveGameStat():
if not activeGames: return None
return max(activeGames)
def bah():
if limit == 0: return "Limit is 0"
if len(activeGames) >= limit:
somestat = getBestActiveGameStat()
if somestat is None:
print "The universe has exploded"
#etc...
What would go in the universe exploding line? If limit is 0, then the function returns. If len(activeGames) >= limit, then there must be at least one active game, so getBestActiveGameStat() can't return None. So, should I even check for it?
The same also happens with something like a while loop which always returns in the loop:
def hmph():
while condition:
if foo: return "yep"
doStuffToMakeFooTrue()
raise SingularityFlippedMyBitsError()
Since I "know" it's impossible, should anything even be there?
If len(activeGames) >= limit, then
there must be at least one active
game, so getBestActiveGameStat() can't
return None. So, should I even check
for it?
Sometimes we make mistakes. You could have a program error now -- or someone could create one later.
Those errors might result in exceptions or failed unit tests. But debugging is expensive; it's useful to have multiple ways to detect errors.
A quickly written assert statement can express an expected invariant to human readers. And when debugging, a failed assertion can pinpoint an error quickly.
Sutter and Alexandrescu address this issue in "C++ Coding Standards." Despite the title, their arguments and guidelines are are language agnostic.
Assert liberally to document internal assumptions and invariants
... Use assert or an equivalent liberally to document assumptions internal to a module ... that must always be true and otherwise represent programming errors.
For example, if the default case in a switch statement cannot occur, add the case with assert(false).
IMHO, the first example is really more a question of how catastrophic failures are presented to the user. In the event that someone does something really silly and sets activeGames to none, most languages will throw a NullPointer/InvalidReference type of exception. If you have a good system for catching these kinds of errors and handling them elegantly, then I would argue that you leave these guards out entirely.
If you have a decent set of unit tests, they will ensure with huge amounts of certainty that this kind of problem does not escape the developers machine.
As for the second one, what you're really guarding against is a race condition. What if the "doStuffToMakeFooTrue()" method never makes foo true? This code will eventually run itself into the ground. Rather than risk that, I'll usually put code like this on a timer. If your language has closures or function pointers (honestly not sure about Python...), you can hide the implementation of the timing logic in a nice helper method, and call it this way:
withTiming(hmph, 30) // run for 30 seconds, then fail
If you don't have closures or function pointers, you'll have to do it the long way everywhere:
stopwatch = new Stopwatch(30)
stopwatch.start()
while stopwatch.elapsedTimeInSeconds() < 30
hmph()
raise OperationTimedOutError()

What are the cons of returning an Exception instance instead of raising it in Python?

I have been doing some work with python-couchdb and desktopcouch. In one of the patches I submitted I wrapped the db.update function from couchdb. For anyone that is not familiar with python-couchdb the function is the following:
def update(self, documents, **options):
"""Perform a bulk update or insertion of the given documents using a
single HTTP request.
>>> server = Server('http://localhost:5984/')
>>> db = server.create('python-tests')
>>> for doc in db.update([
... Document(type='Person', name='John Doe'),
... Document(type='Person', name='Mary Jane'),
... Document(type='City', name='Gotham City')
... ]):
... print repr(doc) #doctest: +ELLIPSIS
(True, '...', '...')
(True, '...', '...')
(True, '...', '...')
>>> del server['python-tests']
The return value of this method is a list containing a tuple for every
element in the `documents` sequence. Each tuple is of the form
``(success, docid, rev_or_exc)``, where ``success`` is a boolean
indicating whether the update succeeded, ``docid`` is the ID of the
document, and ``rev_or_exc`` is either the new document revision, or
an exception instance (e.g. `ResourceConflict`) if the update failed.
If an object in the documents list is not a dictionary, this method
looks for an ``items()`` method that can be used to convert the object
to a dictionary. Effectively this means you can also use this method
with `schema.Document` objects.
:param documents: a sequence of dictionaries or `Document` objects, or
objects providing a ``items()`` method that can be
used to convert them to a dictionary
:return: an iterable over the resulting documents
:rtype: ``list``
:since: version 0.2
"""
As you can see, this function does not raise the exceptions that have been raised by the couchdb server but it rather returns them in a tuple with the id of the document that we wanted to update.
One of the reviewers went to #python on irc to ask about the matter. In #python they recommended to use sentinel values rather than exceptions. As you can imaging just an approach is not practical since there are lots of possible exceptions that can be received. My questions is, what are the cons of using Exceptions over sentinel values besides that using exceptions is uglier?
I think it is ok to return the exceptions in this case, because some parts of the update function may succeed and some may fail. When you raise the exception, the API user has no control over what succeeded already.
Raising an Exception is a notification that something that was expected to work did not work. It breaks the program flow, and should only be done if whatever is going on now is flawed in a way that the program doesn't know how to handle.
But sometimes you want to raise a little error flag without breaking program flow. You can do this by returning special values, and these values can very well be exceptions.
Python does this internally in one case. When you compare two values like foo < bar, the actual call is foo.__lt__(bar). If this method raises an exception, program flow will be broken, as expected. But if it returns NotImplemented, Python will then try bar.__ge__(foo) instead. So in this case returning the exception rather than raising it is used to flag that it didn't work, but in an expected way.
It's really the difference between an expected error and an unexpected one, IMO.
exceptions intended to be raised. It helps with debugging, handling causes of the errors and it's clear and well-established practise of other developers.
I think looking at the interface of the programme, it's not clear what am I supposed to do with returned exception. raise it? from outside of the chain that actually caused it? it seems a bit convoluted.
I'd suggest, returning docid, new_rev_doc tuple on success and propagating/raising exception as it is. Your approach duplicates success and type of 3rd returned value too.
Exceptions cause the normal program flow to break; then exceptions go up the call stack until they're intercept, or they may reach the top if they aren't. Hence they're employed to mark a really special condition that should be handled by the caller. Raising an exception is useful since the program won't continue if a necessary condition has not been met.
In languages that don't support exceptions (like C) you're often forced to check return values of functions to verify everything went on correctly; otherwise the program may misbehave.
By the way the update() is a bit different:
it takes multiple arguments; some may fail, some may succeed, hence it needs a way to communicate results for each arg.
a previous failure has no relation with operations coming next, e.g. it is not a permanent error
In that situation raising an exception would NOT be usueful in an API. On the other hand, if the connection to the db drops while executing the query, then an exception is the way to go (since it's a permament error and would impact all operations coming next).
By the way if your business logic requires all operations to complete successfully and you don't know what to do when an update fails (i.e. your design says it should never happen), feel free to raise an exception in your own code.