Clearing concepts on the exception - python

Let's say I have a function.
def foo(data):
if data:
return data[0]
else:
raise ValueError('data is empty')
def main_foo(..):
ele = foo(data)
Now, i want to catch that exception as one of my friends commented
Please re-raise the errors at main_foo
So does that mean I do something like:
def main_foo( .. ):
try:
ele = foo(data)
except ValueError:
logger.log("exception caught")

If you want to log the occurrence of an exception in main_foo, but let some other function actually handle the exception, do this:
def main_foo():
try:
ele = foo(data)
except ValueError:
logger.log("Exception caught")
raise
This will raise the same exception for the caller of main_foo to deal with.

Related

Why is the Exception not printing?

In the REPL, I can print the string representation of an exception:
>>> print(str(ValueError))
<class 'ValueError'>
>>> print(ValueError)
<class 'ValueError'>
In this simple code, the value is not printing. What am I missing?
First flavor:
try:
raise ValueError
except Exception as e:
print(str(e))
print('We crashed!')
This just outputs We crashed!
The second flavor outputs the same. What happened to print(str(e))?
Second flavor:
def crash():
raise ValueError
try:
crash()
except Exception as e:
print(str(e))
print('We crashed!')
In the REPL, I can print the string representation of an exception:
>>> print(str(ValueError))
<class 'ValueError'>
No, you're printing the string representation of an exception class. When you do
raise ValueError
ValueError isn't actually the exception object that gets raised. Python implicitly raises ValueError() instead, and the str of that is empty.
It does print the exception message, but you have to look closely:
>>> def crash():
... raise ValueError
>>> try:
... crash()
... except Exception as e:
... print(str(e))
... print('We crashed!')
We crashed!
The empty line in front of "We crashed" is the error message (empty string) you supplied when doing raise ValueError. To have the representation of your exceptions use repr instead of str:
>>> try:
... crash()
... except Exception as e:
... print(repr(e))
... print('We crashed!')
ValueError()
We crashed!
Note that raise ValueError is just a shorthand for raise ValueError() (note the paranthesis).
You're not supplying any args to the instance that is eventually created, there's nothing to print.
Supply the message that will eventually get printed out by initializing and supplying it as an argument:
try:
raise ValueError("What value error?")
except Exception as e:
print(str(e))
print('We crashed!')
What value error?
We crashed!
Also, though I know this is just a demonstration, I am obliged to point out that using Exception as the target in your handlers is bad practice :-)
As in user2357112 's answer in your except block your are trying to print an instance of ValueError not the ValueError class. If you want to print the class try something like below.
try:
raise ValueError
except Exception as e:
print(e.__class__)
print('We crashed!')

Pass variable to an exception?

I am trying to learn Python and I want to know if it is possible to pass a variable to an Exception? This is the code I have:
try:
staffId = int(row['staffId'])
openingSalary = int(row['initialSalary'])
monthsWorked = float(row['monthsWorked'])
except CutomException:
pass
class CustomException(ValueError): # raised if data conversion fails
def __init__(self):
print("There was a problem converting data")
I want to pass staffId to the exception so that I can print something like:
print("There was a problem converting data for staff Id: ", staffId)
I tried this with no success: How to pass a variable to an exception when raised and retrieve it when excepted?
The caller of the exception, e.g. the one that raise exception will have to pass an argument to the constructor.
class CustomException(ValueError): # raised if data conversion fails
def __init__(self, message):
self.message = message;
print("There was a problem converting data")
try:
try:
staffId = int(row['staffId'])
openingSalary = int(row['initialSalary'])
monthsWorked = float(row['monthsWorked'])
except ValueError as e:
raise CustomException(e);
except CustomException:
pass
The custom exception will need to be raise'd conditionally by the try block to include the staffId variable. As an example, when the staffId is a str and not an int.
try:
# conditionalize a scenario where you'd want to raise an error
# (e.g. the variable is a string)
if type(staffId) is str:
raise CustomException(staffId)
else:
staffId = int(row['staffId'])
openingSalary = int(row['initialSalary'])
monthsWorked = float(row['monthsWorked'])
except CutomException:
pass
class CustomException(ValueError): # raised if data conversion fails
def __init__(self, id):
print("There was a problem converting data %s" % id)
I think you should handle the exception in the except block and not inside the exception class.
try:
raise CustomException(foo)
except CutomException as e:
print(e.args)
handle_exception()
class CustomException(Exception):
def __init__(self, foo):
super().__init__(foo, bar)

How can I add context to an exception in Python

I would like to add context to an exception like this:
def process(vals):
for key in vals:
try:
do_something(vals[key])
except Exception as ex: # base class. Not sure what to expect.
raise # with context regarding the key that was being processed.
I found a way that is uncharacteristically long winded for Python. Is there a better way than this?
try:
do_something(vals[key])
except Exception as ex:
args = list(ex.args)
if len(args) > 1:
args[0] = "{}: {}".format(key, args[0])
ex.args = tuple(args)
raise # Will re-trhow ValueError with new args[0]
The first item in ex.args is always the message -- if there is any. (Note for some exceptions, such as the one raised by assert False, ex.args is an empty tuple.)
I don't know of a cleaner way to modify the message than reassigning a new tuple to ex.args. (We can't modify the tuple since tuples are immutable).
The code below is similar to yours, except it constructs the tuple without using an intermediate list, it handles the case when ex.args is empty, and to make the code more readable, it hides the boilerplate inside a context manager:
import contextlib
def process(val):
with context(val):
do_something(val)
def do_something(val):
# assert False
return 1/val
#contextlib.contextmanager
def context(msg):
try:
yield
except Exception as ex:
msg = '{}: {}'.format(msg, ex.args[0]) if ex.args else str(msg)
ex.args = (msg,) + ex.args[1:]
raise
process(0)
yields a stack trace with this as the final message:
ZeroDivisionError: 0: division by zero
You could just raise a new exception:
def process(vals):
for key in vals:
try:
do_something(vals[key])
except Exception as ex:
raise Error(key, context=ex)
On Python 3 you don't need to provide the old exception explicitly, it will be available as __context__ attribute on the new exception object and the default exception handler will report it automatically:
def process(vals):
for key in vals:
try:
do_something(vals[key])
except Exception:
raise Error(key)
In you case, you should probably use the explicit raise Error(key) from ex syntax that sets __cause__ attribute on the new exception, see Exception Chaining and Embedded Tracebacks.
If the only issue is the verbosity of the message-amending code in your question; you could encapsulate it in a function:
try:
do_something(vals[key])
except Exception:
reraise_with_context(key=key) # reraise with extra info
where:
import inspect
import sys
def reraise_with_context(**context):
ex = sys.exc_info()[1]
if not context: # use locals from the caller scope
context = inspect.currentframe().f_back.f_locals
extra_info = ", ".join("%s=%s" % item for item in context.items())
amend_message(ex, extra_info)
raise
def amend_message(ex, extra):
msg = '{} with context: {}'.format(ex.args[0], extra) if ex.args else extra
ex.args = (msg,) + ex.args[1:]

What does except really do in Python?

I'm really new in Python and a have no experience with exceptions but I've read all the documentation and couldn't find an answer ... so I'm looking for a deeper view in except's semantics.
When we have for example:
try:
x = 2
except GreaterThanOne:
print("The value is greater than one")
In this case I want the message to be printed.Is there a way for the GreaterThanOne class(exception) to be defined to raise when the entered value is greater than one ?
Ok, let me be more specific ...
Every error raises by a specific rule which should be add in the error attributes, am I right ?
For example:
try:
myvalue = x / y
except ZeroDivisionError:
print("Some error message printed ...")
So when I use this code and enter for y to be 0 the exception ZeroDivisionError will raise ... Can I for example redefine ZeroDivisionError to raise like this but if y is set to be ... 2 or 3 or any other value ?
Input:
x = 10
y = 2
try:
myvalue = x / y
except ZeroDivisionError:
print("division by 2")
Output: division by 2
Here's an example that should help you understand. Run this in your Python interpreter and watch how the exception is raised and caught (or not caught) when you call set_val(2).
# Defining our Exception subclass:
class GreaterThanOne(Exception):
pass
# The global value we pretend to care about:
val = 0
# Function to set a value but possibly raise our new Exception
def set_val(new_val):
if new_val > 1:
raise GreaterThanOne("%d > 1" % new_val)
val = new_val
# Catching exception:
try:
set_val(0)
set_val(1)
set_val(2)
except GreaterThanOne:
print "Whoops - one of those values was greater than one"
# Not catching exception:
set_val(0)
set_val(1)
set_val(2)
set_val(3)
an try-except block catches exception in this block.
try:
#some stuff here
except ExceptionClass as e:
#Exception handling here
the class after the except keyword indicates which kind of exception you want to catch. Usually you give a specific class, like ValueError or KeyError. You can also use the Exception class, to catch any exception. Because all the other exceptionclasses inhert from Exception.
so if you want to use this construct, an exception needs to be raised, Either by a function / method you call, or you raise it yourself with the raise keyword.
like this:
try:
raise KeyError('Just for test')
except KeyError as e:
#Exception handling here
The try except doesn't automagically inspect the whole code between it, it just looks for exceptions... Or to be more specific, it looks for those exceptions you tell it to look for.
Of course you can also inspect the exception instance.
try:
raise KeyError('Just for test')
except KeyError as e:
print e.args
For more information, please see:
http://docs.python.org/2/tutorial/errors.html

How to write multiple try statements in one block in python?

I want to do:
try:
do()
except:
do2()
except:
do3()
except:
do4()
If do() fails, execute do2(), if do2() fails too, exceute do3() and so on.
best Regards
If you really don't care about the exceptions, you could loop over cases until you succeed:
for fn in (do, do2, do3, do4):
try:
fn()
break
except:
continue
This at least avoids having to indent once for every case. If the different functions need different arguments you can use functools.partial to 'prime' them before the loop.
I'd write a quick wrapper function first() for this.
usage: value = first([f1, f2, f3, ..., fn], default='All failed')
#!/usr/bin/env
def first(flist, default=None):
""" Try each function in `flist` until one does not throw an exception, and
return the return value of that function. If all functions throw exceptions,
return `default`
Args:
flist - list of functions to try
default - value to return if all functions fail
Returns:
return value of first function that does not throw exception, or
`default` if all throw exceptions.
TODO: Also accept a list of (f, (exceptions)) tuples, where f is the
function as above and (exceptions) is a tuple of exceptions that f should
expect. This allows you to still re-raise unexpected exceptions.
"""
for f in flist:
try:
return f()
except:
continue
else:
return default
# Testing.
def f():
raise TypeError
def g():
raise IndexError
def h():
return 1
# We skip two exception-throwing functions and return value of the last.
assert first([f, g, h]) == 1
assert first([f, g, f], default='monty') == 'monty'
It seems like a really odd thing to want to do, but I would probably loop over the functions and break out when there were no exception raised:
for func in [do, do2, do3]:
try:
func()
except Exception:
pass
else:
break
Here is the simplest way I found, just embed the try under the previous except.
try:
do()
except:
try:
do2()
except:
do3()
You should specify the type of the exception you are trying to catch each time.
try:
do()
except TypeError: #for example first one - TypeError
do_2()
except KeyError: #for example second one - KeyError
do_3()
and so on.
if you want multiple try statments you can do it like this, including the except statement. Extract (refactor) your statements. And use the magic of and and or to decide when to short-circuit.
def a():
try: # a code
except: pass # or raise
else: return True
def b():
try: # b code
except: pass # or raise
else: return True
def c():
try: # c code
except: pass # or raise
else: return True
def d():
try: # d code
except: pass # or raise
else: return True
def main():
try:
a() and b() or c() or d()
except:
pass
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise

Categories

Resources