Exception handling is ignored with try-except around a function definition - python

My message "Divide by 0 error" is not going through, instead I'm getting a normal ZeroDivisionError.
#!/usr/bin/python
t = raw_input("do you want to play a game?[y/n]" )
#r = raw_input("Please enter a number")
#e = raw_input("Please enter a number again")
try:
def di (a, b):
return a/b
except ZeroDivisionError:
print "Divide by 0 Error"
while t == "y":
u = raw_input("Please enter / sign ")
if u == "/":
r = int(raw_input("Please enter a number"))
try:
e = int(raw_input("Please enter a number again"))
print "the answer is", di(r, e)
t = raw_input("do you want to play a game?[y/n]" )
except ValueError:
t = raw_input( "Invalid input, must be a number. Press yes to continue, no stop")

Look at the code more closely:
try:
def di (a, b):
return a/b
except ZeroDivisionError:
print "Divide by 0 Error"
Your try/except block includes the entire function definition: it applies specifically to defining the function. There is no exception block active while the function is executing from a call.
Use this instead:
def di (a, b):
try:
return a/b
except ZeroDivisionError:
print "Divide by 0 Error"

My take on this question (clearly interesting enough to provoke multiple answers, well done) is to remember that in Python class and function definitions are executed like most other statements.
try:
def di (a, b):
return a/b
except ZeroDivisionError:
print "Divide by 0 Error"
says:
"Attempt to define a function called di that returns the dividend of its arguments. If defining the function raises a ZeroDivisionError exception, print an explanatory message." No exception will be raised.
I suspect what is required is instead:
"Define a function that attempts to return the dividend of its arguments. If the division raises a ZeriDivisionError exception the function prints an explanatory message and returns None." So the def should be around the whole function's logic:
def di(a, b):
try:
return a/b
except ZeroDivisionError:
print "Divide by 0 error"
As a more general point of program design, such a function is somewhat badly-coupled. Python programmers would probably conclude, unless constrained by other factors, that it was simpler to leave the exception uncaught: presumably the caller currently has to test for None to determine whether an exception occurred, so why not just trap the exception wherever it actually has to be handled, and handle it there?
Indicating the invalidity of data by returning an object of a different type makes for complex and hard-to-read code, and is probably best avoided in the long term. Perfectly acceptable for a learning exercise, though!

TLDR: Move the exception handler into the function, where the exception actually occurs:
def di(a, b):
try:
return a/b
except ZeroDivisionError:
print("Divide by 0 Error")
Function definitions in Python are executed, and this execution can have side-effects -- such as modifying objects or raising exception. However, on definition only the function signature is run immediately; the body is stored and only run when the function is called.
>>> def func(arg=print("default evaluated")):
... print("body evaluated")
...
default evaluated
>>> func
<function __main__.func(arg=None)>
>>> func()
body evaluated
When you define a function inside an exception handler, this exception handler only receives exceptions raised from evaluating the signature. For example, computing a default argument may raise a ZeroDivisionError:
>>> def invalid_def(arg=10/0):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
An exception handler for this case is rarely what you want. It can be useful to conditionally define a function, e.g. if a NameError indicates a dependency is not available.
Commonly you want to handle an exception originating in the body of a function. These are raised whenever the function is actually called:
>>> def invalid_call():
... return 10 / 0
...
>>> invalid_call()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in invalid_call
ZeroDivisionError: division by zero
>>> invalid_call()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in invalid_call
ZeroDivisionError: division by zero
There are two ways to handle such exceptions: externally or internally.
External handling requires an exception handler at each call site. This is more flexible but requires boilerplate, since it requires an exception handler for each call.
>>> try:
... invalid_call()
... except ZeroDivisionError:
... print("suppressed an external error")
Internal handling requires an exception handler in the body. This is less flexible but needs no boilerplate, since one exception handler covers all cases.
>>> def valid_call():
... try:
... return 10 / 0
... except ZeroDivisionError:
... print("suppressed an internal error")
Both approaches are valid; which to select depends on whether you rather need usability or re-usability. Notably, it is not uncommon to combine both approaches: an internal handler generalises the exception, and external handler expects only general exceptions.
For example, in your case that would allow to handle multiple operations:
def div(a, b):
try:
return a / b
# convert operation-specific exception to general one
except ZeroDivisionError:
raise ValueError("Cannot divide by 0")
# may be another operation -- add, mul, sub, ...
operation = div
try:
operation(10, 0)
# only handle exception general to all operations
except ValueError as err:
print("Invalid values: %s" % err)

Related

writing a function that catches multiple exceptions in python

I am required to write a function that takes a simple mathematical formula as a string as an argument. The function should then return the result of that formula. For example, for the input "2 + 3" the function should return 5.
A string is considered a valid formula if it has the format . Note that operator and integer are separated by whitespace.
A valid operator is either +, -, * or /
If a string doesn't consist of three parts (integer operator integer), the function should raise a ValueError with the message "Formula must be of the following format: ."
If the first part or the last part of the input string can't be converted to integers, the function should raise a ValueError with the message "Expected two integers."
If the second part of the string is not one of the valid operators, the function should raise a ValueError with the message "Invalid operator ''. Expected one of these operators: +, -, *, /."
If the second integer is zero and the operator is /, the function should raise a ZeroDivisionError with the message "Division by zero not possible."
So far I've managed to split the string by whitespace and convert the [0] and [2] indexes to integers to be used in solving the respective mathematical equations, and I've also written a try: except: block that successfully catches invalid operators and returns the desired error message. My problem is going on to accommodate the other exceptions as outlined in the conditions, although I've written code that attempts to catch the exceptions and print the relevant error messages, it isn't working and I'm still getting the default internal python error messages. I'm assuming something in my approach is off, maybe the order that the try: except blocks are written in? something with the indenting? I'm new to this so any pointers or advice would be much appreciated.
def formula_from_string(formula):
valid_operators = '+-*/'
chopped=formula.split()
equa=int(chopped[0]),chopped[1],int(chopped[2])
subtraction=equa[0]-equa[2]
addition=equa[0]+equa[2]
division=equa[0]/equa[2]
multiplication=equa[0]*equa[2]
if chopped[1]=='+':
return(addition)
elif chopped[1]=='-':
return(subtraction)
elif chopped[1]=='*':
return(multiplication)
elif chopped[1]=='/':
return(division)
try:
if chopped[1] not in valid_operators:
invalid=chopped[1]
raise ValueError
except ValueError:
print('Value Error:')
return("Invalid operator '"+invalid+"'. Expected one of these operators: +, -, *, /.")
try:
if chopped[0] or chopped[2] != int:
raise ValueError
except ValueError:
print('Value Error:')
return('Expected two integers.')
try:
if equa[1]=='/' and equa[2]==0:
raise ZeroDivisionError
except ZeroDivisionError:
print('ZeroDivisionError:')
return('Division by zero not possible.')
try:
if chopped <=1 or chopped >=2:
raise ValueError
except ValueError:
print('ValueError:')
return('Formula must be of the following format: <integer> <operator> <integer>.')
This code should helps you.
learn about Regex (Regular Expressions)
See Regular expression operations
import re
def formula_from_string(formula):
valid_operators = '+-*/'
pattern = re.compile(r'^(\d+)(?:\s+)?([*/+\-^])(?:\s+)?(\d+)$')
try:
if match := pattern.search(formula):
operator = match.group(2)
if operator not in valid_operators:
raise ValueError(f"Invalid operator {repr(operator)}. Expected one of these operators: +, -, *, /.")
else:
raise ValueError('Formula must be of the following format: <integer> <operator> <integer>.')
return eval(formula) # Safe call
except (ValueError, ZeroDivisionError) as e:
print(e)
# Uncomment to see output
# formula_from_string('3 / 2') # 1.5
# formula_from_string('3 ^ 2') # ValueError Invalid operator
# formula_from_string('a / 2') # ValueError Formula must be...
# formula_from_string('3 / 0') # ZeroDivisionError

How to code try/except in one place but for many evaluations

I have a snippet of code like this, with try except wrapped into a function tryprint(), so I can call upon this function multiple times instead of keep writing try except for a dozen of evaluations.
import numpy as np
def tryprint(fun):
try:
fun
except ValueError:
return 'ValueError'
tryprint(np.dot([1,1],[2]))
Here because the input is wrong by intention, I expected calling tryprint() on an evaluation would print just 'ValueError' however it resulted in the error msg I tried to avoid:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-47-a1b49d56a970> in <module>()
4 except ValueError:
5 return 'ValueError'
----> 6 tryprint(np.dot([1,1],[2]))
What do I need to do to make this function only print 'ValueError' if input is wrong, instead of printing full error msg and stop the cell from running? Thanks.
Another solution is to pass the function and its args separately:
import numpy as np
def tryprint(fun, *args):
try:
retVal = fun(*args)
except ValueError:
return 'ValueError'
return retVal
print(tryprint(np.dot, [1,1],[2]))

Function argument in python : not raising NameError

The way I learn in python argument to use a function with an argument is as follow:
def res(arg1, arg2):
try:
print(a+b) # it prints result.. shouldn't code be break here?
return a + b # this also returns a result.
except NameError:
return "failed."
a = 2
b = 3
print(res(a, b))
I am expecting that in such case function should raise NameError and it should be handled in except block but it is not raising any kind of error instead it works with both arg1+arg2 and a +b am I missing something here? need any kind of help to understand and overcome this.

How to better write multiple exceptions with redundant code in Python?

How can I better write the following snippet in Python:
try:
statement-1
except Exception1:
codeblock-1
codeblock-2
except Exception2:
codeblock-2
Just to be clear, I want to execute two codeblocks when the first exception occurs, while only the latter of these two codeblocks when the second exception occurs.
You have two options, as I see it; either:
Extract codeblock-2 into a function and just call it (you repeat only one line this way); or
Catch both exceptions in the same except, then handle the two cases appropriately by checking the type of the caught exception.
Note that these aren't mutually exclusive, and the second approach is probably more readable if combined with the first. A snippet of the latter:
try:
statement-1
except (Exception1, Exception2) as exc:
if isinstance(exc, Exception1):
codeblock-1
codeblock-2
In action:
>>> def test(x, y):
try:
return x / y
except (TypeError, ZeroDivisionError) as exc:
if isinstance(exc, TypeError):
print "We got a type error"
print "We got a type or zero division error"
>>> test(1, 2.)
0.5
>>> test(1, 'foo')
We got a type error
We got a type or zero division error
>>> test(1, 0)
We got a type or zero division error
I would just straightforwardly use local function:
def exception_reaction():
codeblock2()
try:
statement1()
except Exception1:
codeblock1()
exception_reaction()
except Exception2:
exception_reaction()

Handing out Exceptions in Generators [duplicate]

This is a follow-up to Handle an exception thrown in a generator and discusses a more general problem.
I have a function that reads data in different formats. All formats are line- or record-oriented and for each format there's a dedicated parsing function, implemented as a generator. So the main reading function gets an input and a generator, which reads its respective format from the input and delivers records back to the main function:
def read(stream, parsefunc):
for record in parsefunc(stream):
do_stuff(record)
where parsefunc is something like:
def parsefunc(stream):
while not eof(stream):
rec = read_record(stream)
do some stuff
yield rec
The problem I'm facing is that while parsefunc can throw an exception (e.g. when reading from a stream), it has no idea how to handle it. The function responsible for handling exceptions is the main read function. Note that exceptions occur on a per-record basis, so even if one record fails, the generator should continue its work and yield records back until the whole stream is exhausted.
In the previous question I tried to put next(parsefunc) in a try block, but as turned out, this is not going to work. So I have to add try-except to the parsefunc itself and then somehow deliver exceptions to the consumer:
def parsefunc(stream):
while not eof(stream):
try:
rec = read_record()
yield rec
except Exception as e:
?????
I'm rather reluctant to do this because
it makes no sense to use try in a function that isn't intended to handle any exceptions
it's unclear to me how to pass exceptions to the consuming function
there going to be many formats and many parsefunc's, I don't want to clutter them with too much helper code.
Has anyone suggestions for a better architecture?
A note for googlers: in addition to the top answer, pay attention to senderle's and Jon's posts - very smart and insightful stuff.
You can return a tuple of record and exception in the parsefunc and let the consumer function decide what to do with the exception:
import random
def get_record(line):
num = random.randint(0, 3)
if num == 3:
raise Exception("3 means danger")
return line
def parsefunc(stream):
for line in stream:
try:
rec = get_record(line)
except Exception as e:
yield (None, e)
else:
yield (rec, None)
if __name__ == '__main__':
with open('temp.txt') as f:
for rec, e in parsefunc(f):
if e:
print "Got an exception %s" % e
else:
print "Got a record %s" % rec
Thinking deeper about what would happen in a more complex case kind of vindicates the Python choice of avoiding bubbling exceptions out of a generator.
If I got an I/O error from a stream object the odds of simply being able to recover and continue reading, without the structures local to the generator being reset in some way, would be low. I would somehow have to reconcile myself with the reading process in order to continue: skip garbage, push back partial data, reset some incomplete internal tracking structure, etc.
Only the generator has enough context to do that properly. Even if you could keep the generator context, having the outer block handle the exceptions would totally flout the Law of Demeter. All the important information that the surrounding block needs to reset and move on is in local variables of the generator function! And getting or passing that information, though possible, is disgusting.
The resulting exception would almost always be thrown after cleaning up, in which case the reader-generator will already have an internal exception block. Trying very hard to maintain this cleanliness in the brain-dead-simple case only to have it break down in almost every realistic context would be silly. So just have the try in the generator, you are going to need the body of the except block anyway, in any complex case.
It would be nice if exceptional conditions could look like exceptions, though, and not like return values. So I would add an intermediate adapter to allow for this: The generator would yield either data or exceptions and the adapter would re-raise the exception if applicable. The adapter should be called first-thing inside the for loop, so that we have the option of catching it within the loop and cleaning up to continue, or breaking out of the loop to catch it and and abandon the process. And we should put some kind of lame wrapper around the setup to indicate that tricks are afoot, and to force the adapter to get called if the function is adapting.
That way each layer is presented errors that it has the context to handle, at the expense of the adapter being a tiny bit intrusive (and perhaps also easy to forget).
So we would have:
def read(stream, parsefunc):
try:
for source in frozen(parsefunc(stream)):
try:
record = source.thaw()
do_stuff(record)
except Exception, e:
log_error(e)
if not is_recoverable(e):
raise
recover()
except Exception, e:
properly_give_up()
wrap_up()
(Where the two try blocks are optional.)
The adapter looks like:
class Frozen(object):
def __init__(self, item):
self.value = item
def thaw(self):
if isinstance(value, Exception):
raise value
return value
def frozen(generator):
for item in generator:
yield Frozen(item)
And parsefunc looks like:
def parsefunc(stream):
while not eof(stream):
try:
rec = read_record(stream)
do_some_stuff()
yield rec
except Exception, e:
properly_skip_record_or_prepare_retry()
yield e
To make it harder to forget the adapter, we could also change frozen from a function to a decorator on parsefunc.
def frozen_results(func):
def freezer(__func = func, *args, **kw):
for item in __func(*args, **kw):
yield Frozen(item)
return freezer
In which case we we would declare:
#frozen_results
def parsefunc(stream):
...
And we would obviously not bother to declare frozen, or wrap it around the call to parsefunc.
Without knowing more about the system, I think it's difficult to tell what approach will work best. However, one option that no one has suggested yet would be to use a callback. Given that only read knows how to deal with exceptions, might something like this work?
def read(stream, parsefunc):
some_closure_data = {}
def error_callback_1(e):
manipulate(some_closure_data, e)
def error_callback_2(e):
transform(some_closure_data, e)
for record in parsefunc(stream, error_callback_1):
do_stuff(record)
Then, in parsefunc:
def parsefunc(stream, error_callback):
while not eof(stream):
try:
rec = read_record()
yield rec
except Exception as e:
error_callback(e)
I used a closure over a mutable local here; you could also define a class. Note also that you can access the traceback info via sys.exc_info() inside the callback.
Another interesting approach might be to use send. This would work a little differently; basically, instead of defining a callback, read could check the result of yield, do a lot of complex logic, and send a substitute value, which the generator would then re-yield (or do something else with). This is a bit more exotic, but I thought I'd mention it in case it's useful:
>>> def parsefunc(it):
... default = None
... for x in it:
... try:
... rec = float(x)
... except ValueError as e:
... default = yield e
... yield default
... else:
... yield rec
...
>>> parsed_values = parsefunc(['4', '6', '5', '5h', '22', '7'])
>>> for x in parsed_values:
... if isinstance(x, ValueError):
... x = parsed_values.send(0.0)
... print x
...
4.0
6.0
5.0
0.0
22.0
7.0
On it's own this is a bit useless ("Why not just print the default directly from read?" you might ask), but you could do more complex things with default inside the generator, resetting values, going back a step, and so on. You could even wait to send a callback at this point based on the error you receive. But note that sys.exc_info() is cleared as soon as the generator yields, so you'll have to send everything from sys.exc_info() if you need access to the traceback.
Here's an example of how you might combine the two options:
import string
digits = set(string.digits)
def digits_only(v):
return ''.join(c for c in v if c in digits)
def parsefunc(it):
default = None
for x in it:
try:
rec = float(x)
except ValueError as e:
callback = yield e
yield float(callback(x))
else:
yield rec
parsed_values = parsefunc(['4', '6', '5', '5h', '22', '7'])
for x in parsed_values:
if isinstance(x, ValueError):
x = parsed_values.send(digits_only)
print x
An example of a possible design:
from StringIO import StringIO
import csv
blah = StringIO('this,is,1\nthis,is\n')
def parse_csv(stream):
for row in csv.reader(stream):
try:
yield int(row[2])
except (IndexError, ValueError) as e:
pass # don't yield but might need something
# All others have to go up a level - so it wasn't parsable
# So if it's an IOError you know why, but this needs to catch
# exceptions potentially, just let the major ones propogate
for record in parse_csv(blah):
print record
I like the given answer with the Frozen stuff. Based on that idea I came up with this, solving two aspects I did not yet like. The first was the patterns needed to write it down. The second was the loss of the stack trace when yielding an exception. I tried my best to solve the first by using decorators as good as possible. I tried keeping the stack trace by using sys.exc_info() instead of the exception alone.
My generator normally (i.e. without my stuff applied) would look like this:
def generator():
def f(i):
return float(i) / (3 - i)
for i in range(5):
yield f(i)
If I can transform it into using an inner function to determine the value to yield, I can apply my method:
def generator():
def f(i):
return float(i) / (3 - i)
for i in range(5):
def generate():
return f(i)
yield generate()
This doesn't yet change anything and calling it like this would raise an error with a proper stack trace:
for e in generator():
print e
Now, applying my decorators, the code would look like this:
#excepterGenerator
def generator():
def f(i):
return float(i) / (3 - i)
for i in range(5):
#excepterBlock
def generate():
return f(i)
yield generate()
Not much change optically. And you still can use it the way you used the version before:
for e in generator():
print e
And you still get a proper stack trace when calling. (Just one more frame is in there now.)
But now you also can use it like this:
it = generator()
while it:
try:
for e in it:
print e
except Exception as problem:
print 'exc', problem
This way you can handle in the consumer any exception raised in the generator without too much syntactic hassle and without losing stack traces.
The decorators are spelled out like this:
import sys
def excepterBlock(code):
def wrapper(*args, **kwargs):
try:
return (code(*args, **kwargs), None)
except Exception:
return (None, sys.exc_info())
return wrapper
class Excepter(object):
def __init__(self, generator):
self.generator = generator
self.running = True
def next(self):
try:
v, e = self.generator.next()
except StopIteration:
self.running = False
raise
if e:
raise e[0], e[1], e[2]
else:
return v
def __iter__(self):
return self
def __nonzero__(self):
return self.running
def excepterGenerator(generator):
return lambda *args, **kwargs: Excepter(generator(*args, **kwargs))
(I answered the other question linked in the OP but my answer applies to this situation as well)
I have needed to solve this problem a couple of times and came upon this question after a search for what other people have done.
One option- which will probably require refactoring things a little bit- would be to simply create an error handling generator, and throw the exception in the generator (to another error handling generator) rather than raise it.
Here is what the error handling generator function might look like:
def err_handler():
# a generator for processing errors
while True:
try:
# errors are thrown to this point in function
yield
except Exception1:
handle_exc1()
except Exception2:
handle_exc2()
except Exception3:
handle_exc3()
except Exception:
raise
An additional handler argument is provided to the parsefunc function so it has a place to put the errors:
def parsefunc(stream, handler):
# the handler argument fixes errors/problems separately
while not eof(stream):
try:
rec = read_record(stream)
do some stuff
yield rec
except Exception as e:
handler.throw(e)
handler.close()
Now just use almost the original read function, but now with an error handler:
def read(stream, parsefunc):
handler = err_handler()
for record in parsefunc(stream, handler):
do_stuff(record)
This isn't always going to be the best solution, but it's certainly an option, and relatively easy to understand.
About your point of propagating exception from generator to consuming function,
you can try to use an error code (set of error codes) to indicate the error.
Though not elegant that is one approach you can think of.
For example in the below code yielding a value like -1 where you were expecting
a set of positive integers would signal to the calling function that there was
an error.
In [1]: def f():
...: yield 1
...: try:
...: 2/0
...: except ZeroDivisionError,e:
...: yield -1
...: yield 3
...:
In [2]: g = f()
In [3]: next(g)
Out[3]: 1
In [4]: next(g)
Out[4]: -1
In [5]: next(g)
Out[5]: 3
Actually, generators are quite limited in several aspects. You found one: the raising of exceptions is not part of their API.
You could have a look at the Stackless Python stuff like greenlets or coroutines which offer a lot more flexibility; but diving into that is a bit out of scope here.

Categories