Is it possible in Python, to catch an exception at the current function level, but not from called sub functions?
Please consider this example:
def func(some_dict):
print(some_dict["special_value"])
some_dict = {
"general_value": True,
# "special_value": True
}
try:
if some_dict["general_value"]:
func(some_dict)
except KeyError:
print("General value not set")
I would like to catch the KeyError that if some_dict["general_value"]: might throw, but raise any KeyErrors from within func(...)
However, the example above will show General value not set although the key general_value is set.
I can think of the following workaround, but I'm wondering if there's a better way
temp_value = None
try:
temp_value = some_dict["general_value"]
except KeyError:
print("General value not set")
if temp_value:
func(some_dict)
Another way to ask this question: Is it possible to exclude certain parts within a try/except block from being caught?
Your workaround is the right general idea, but can be done more neatly as:
try:
some_dict["general_value"]
except KeyError:
print("General value not set")
else:
func(some_dict)
or perhaps:
if "general_value" in some_dict:
func(some_dict)
else:
print("General value not set")
which seems a little clearer to me.
you could use a nested try/except - but this is not recommended.
try:
if some_dict["general_value"]:
try:
func(some_dict)
except KeyError:
print("special error")
except KeyError:
print("general value not set")
Related
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.
I have piece of cake that is returning None in case exception is caught.
def getObject(ver):
av = None
try:
av = getTestObject(ver)
except exceptions.ObjectNotFound, err:
_log.info("%r not Found", obj.fromTestObject(ver))
return av
or this is better ?
def getObject(ver):
try:
av = getTestObject(ver)
except exceptions.ObjectNotFound, err:
_log.info("%r not Found", obj.fromTestObject(ver))
else:
return av
A shorter (more Pythonic, IMHO) equivalent to your first code snippet is as follows:
def getObject(ver):
try:
return getTestObject(ver)
except exceptions.ObjectNotFound, err:
_log.info("%r not Found", obj.fromTestObject(ver))
This works because python functions implicitly return None if control reaches the end of the function without encountering a return (as would be the case if your exception is caught).
First code is better.
Reasons:
Exception may occur before the return value is set to variable av.
In first code, None will be returned if exception occured. In second code, return value is undefined if exception occured.
None is better than undefined as return value.
I wrote a crawler to fetch information out of an Q&A website. Since not all the fields are presented in a page all the time, I used multiple try-excepts to handle the situation.
def answerContentExtractor( loginSession, questionLinkQueue , answerContentList) :
while True:
URL = questionLinkQueue.get()
try:
response = loginSession.get(URL,timeout = MAX_WAIT_TIME)
raw_data = response.text
#These fields must exist, or something went wrong...
questionId = re.findall(REGEX,raw_data)[0]
answerId = re.findall(REGEX,raw_data)[0]
title = re.findall(REGEX,raw_data)[0]
except requests.exceptions.Timeout ,IndexError:
print >> sys.stderr, URL + " extraction error..."
questionLinkQueue.task_done()
continue
try:
questionInfo = re.findall(REGEX,raw_data)[0]
except IndexError:
questionInfo = ""
try:
answerContent = re.findall(REGEX,raw_data)[0]
except IndexError:
answerContent = ""
result = {
'questionId' : questionId,
'answerId' : answerId,
'title' : title,
'questionInfo' : questionInfo,
'answerContent': answerContent
}
answerContentList.append(result)
questionLinkQueue.task_done()
And this code, sometimes, may or may not, gives the following exception during runtime:
UnboundLocalError: local variable 'IndexError' referenced before assignment
The line number indicates the error occurs at the second except IndexError:
Thanks everyone for your suggestions, Would love to give the marks that you deserve, too bad I can only mark one as the correct answer...
In Python 2.x, the line
except requests.exceptions.Timeout, IndexError:
is equivalent to
except requests.exceptions.Timeout as IndexError:
Thus, the exception caught by requests.exceptions.Timeout is assigned to IndexError. A simpler example:
try:
true
except NameError, IndexError:
print IndexError
#name 'true' is not defined
To catch multiple exceptions, put the names in parentheses:
except (requests.exceptions.Timeout, IndexError):
Later, an UnboundLocalError can occur because the assignment to IndexError makes it a local variable (shadowing the builtin name):
>>> 'IndexError' in answerContentExtractor.func_code.co_varnames
True
So, if requests.exceptions.Timeout was not raised, IndexError will not have been (incorrectly) defined when the code attempts except IndexError:.
Again, a simpler example:
def func():
try:
func # defined, so the except block doesn't run,
except NameError, IndexError: # so the local `IndexError` isn't assigned
pass
try:
[][1]
except IndexError:
pass
func()
#UnboundLocalError: local variable 'IndexError' referenced before assignment
In 3.x, the problem will occur (after fixing the except syntax, which makes the error more obvious) even if the first exception is caught. This is because the local name IndexError will then be explicitly deld after the first try/except block.
When you say
except requests.exceptions.Timeout ,IndexError:
Python will except requests.exceptions.Timeout error and the error object will be IndexError. It should have been something like this
except (requests.exceptions.Timeout ,IndexError) as e:
except requests.exceptions.Timeout ,IndexError:
means same as except requests.exceptions.Timeout as IndexError
You should use
except (requests.exceptions.Timeout, IndexError):
instead
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:]
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