Python3 raise exception on multiple failures, with traceback - python

In python 3, exceptions can be chained, like this:
try:
7/0
except ZeroDivisionError:
raise ValueError("Invalid division")
# Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
# ZeroDivisionError: division by zero
#
# During handling of the above exception, another exception occurred:
#
# Traceback (most recent call last):
# File "<stdin>", line 4, in <module>
# ValueError: Invalid division
What if I have the following situation:
A class with a method that may either succeed or throw an error.
I have a bunch of instances of that class in a list.
Somewhere I call that method for every instance on the list.
It is enough for one of them to succeed. If every instance fails I throw an error.
Now, if I have to throw an error, I want to print all exceptions thrown during the test of each instance in the list. Code to clarify:
instances_to_test = [instance0, instance1, instance2]
failures = [] # List of all the thrown exceptions
success = False # True if one of the instances in the list succeeds
for instance in instances_to_test:
try:
instance.method_may_raise() # May raise an Error
success = True
break
except Exception as e:
failures.append(e)
if not success:
# ... What to do here ...
An ideal solution to the line in the comment would look something like:
if not success:
raise MultipleFailException("Could not find an adequate instance", parents=failures)
# 0 ------------------------------------
#
# Traceback (most recent call last):
# File "<stdin>", line 6, in <module>
# ValueError: Message of instance0
#
# 1 ------------------------------------
#
# Traceback (most recent call last):
# File "<stdin>", line 6, in <module>
# ValueError: Message of instance1
#
# 2 ------------------------------------
# Traceback (most recent call last):
# File "<stdin>", line 6, in <module>
# ValueError: Message of instance2
#
# During handling of the above exception(s), another exception occurred:
#
# Traceback (most recent call last):
# File "<stdin>", line 12, in <module>
# MultipleFailException: Could not find an adequate instance
Can this be done in a 'clean' way or should I fiddle with the stack trace like in the python 2 days?

Related

Wolfram Alpha Application Programming Interface Not Working

code:-
import wolframalpha
wolframappid = wolframalpha.Client("XXXXXX-XXXXXXXXXX")
res = wolframappid.query('temperature in mumbai')
answer = next(res.results).text
print(answer)
result:-
Traceback (most recent call last):
File "c:/Users/Harsh/Desktop/Harsh Python Projects/Test.py", line 5, in <module>
answer = next(res.results).text
File "C:\Users\Harsh\AppData\Roaming\Python\Python36\site-packages\wolframalpha\__init__.py", line 166, in text
return next(iter(self.subpod)).plaintext
ValueError: dictionary update sequence element #0 has length 1; 2 is required
any ideas how to solve this issue? i have also tried next(res.results).plaintext but another error pops up..
code:-
import wolframalpha
wolframappid = wolframalpha.Client("XXXXXX-XXXXXXXXXX")
res = wolframappid.query('temperature in mumbai')
answer = next(res.results).plaintext
print(answer)
result:-
Traceback (most recent call last):
File "C:\Users\Harsh\AppData\Roaming\Python\Python36\site-packages\wolframalpha\__init__.py", line 86, in __getattr__
val = self[name] if name in self else self[attr_name]
KeyError: '#plaintext'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "c:/Users/Harsh/Desktop/Harsh Python Projects/Test.py", line 5, in <module>
answer = next(res.results).plaintext
File "C:\Users\Harsh\AppData\Roaming\Python\Python36\site-packages\wolframalpha\__init__.py", line 88, in __getattr__
raise AttributeError(name)
AttributeError: plaintext
thanks in advance...

How to obtain full stack trace after exception from function passed as parameter?

I have wrapper function, that takes other function as parameter, catches an exception and does something with it:
def exceptionCatchingWrapper(funcToCall,destForException,*args,**kwargs):
try:
r=funcToCall(*args,**kwargs)
except:
destForException["exc_info"]=sys.exc_info()
else:
return r
I realized that when an exception is caught, the stack trace taken from sys.exc_info() contains only information about exceptionCatchingWrapper() itself and nothing deeper. Is it possible and how to obtain full stack trace after such call?
import traceback
def a(x):
b(x)
def b(x):
x/0
d = {}
exceptionCatchingWrapper(a, d, 10)
Traceback is stored in the dictionary:
>>> traceback.print_tb(d['exc_info'][2]
File "<stdin>", line 3, in exceptionCatchingWrapper
File "<stdin>", line 2, in a
File "<stdin>", line 2, in b
>>> traceback.print_exception(d['exc_info'][0],d['exc_info'][1],d['exc_info'][2])
Traceback (most recent call last):
File "<stdin>", line 3, in exceptionCatchingWrapper
File "<stdin>", line 2, in a
File "<stdin>", line 2, in b
ZeroDivisionError: integer division or modulo by zero
More information in the traceback module documentation.
Not sure if this is what you need but these might be a way you can print the traceback:
import traceback
try:
s += 1 #this doesnt exist yet
except:
a = traceback.format_exc()
print a
-or-
import traceback, sys
def DummyFunc2():
s += 1 #this doesnt exist yet
def DummyFunc1():
DummyFunc2()
try:
DummyFunc1()
except:
_, err, tb = sys.exc_info()
tb_lines = traceback.extract_tb(tb)
for idx, trace in enumerate( traceback.format_list(tb_lines) ):
print "[INDEX %d]\n%s" % (idx,trace)
print err
output:
>>>
[INDEX 0]
File "C:/Python27/Lib/site-packages/xy/printtrace.py", line 9, in <module>
DummyFunc1()
[INDEX 1]
File "C:/Python27/Lib/site-packages/xy/printtrace.py", line 6, in DummyFunc1
DummyFunc2()
[INDEX 2]
File "C:/Python27/Lib/site-packages/xy/printtrace.py", line 4, in DummyFunc2
s += 1 #this doesnt exist yet
local variable 's' referenced before assignment
>>>

Case-insensitive language-to-iso code?

Is there a way to simplify the following:
>>> pycountry.languages.get(name='english')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Python/2.7/site-packages/pycountry/db.py", line 114, in get
return self.indices[field][value]
KeyError: 'english'
>>> pycountry.languages.get(name='ENGLISH')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Python/2.7/site-packages/pycountry/db.py", line 114, in get
return self.indices[field][value]
KeyError: 'ENGLISH'
>>> pycountry.languages.get(name='English')
<pycountry.db.Language object at 0x1096374d0>
In the above, 'English' is the only item that doesn't result in an Exception.

very simple python 3 try except ValueError not tripping

trying to get my try except block to work.
import sys
def main():
try:
test = int("hello")
except ValueError:
print("test")
raise
main()
output is
C:\Python33>python.exe test.py
test
Traceback (most recent call last):
File "test.py", line 10, in <module>
main()
File "test.py", line 5, in main
test = int("hello")
ValueError: invalid literal for int() with base 10: 'hello'
C:\Python33>
would like the except to trip
You are reraising the exception. It's working as designed.
The test is printed right at the top there before the traceback, but you used raise so the exception still is causing a traceback:
>>> def main():
... try:
... test = int("hello")
... except ValueError:
... print("test")
... raise
...
>>> main()
test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in main
ValueError: invalid literal for int() with base 10: 'hello'
Remove the raise and just the test print remains:
>>> def main():
... try:
... test = int("hello")
... except ValueError:
... print("test")
...
>>> main()
test

"Uncatching" an exception in python

How should I "rethrow" an exception, that is, suppose:
I try something in my code, and unfortunately it fails.
I try some "clever" workaround, which happens to also fail this time
If I throw the exception from the (failing) workaround, it's going to be pretty darn confusing for the user, so I think it may be best to rethrow the original exception (?), with the descriptive traceback it comes with (about the actual problem)...
Note: the motivating example for this is when calling np.log(np.array(['1'], dtype=object)), where it tries a witty workaround and gives an AttributeError (it's "really" a TypeError).
One way I can think of is just to re-call the offending function, but this seems doged (for one thing theoretically the original function may exert some different behaviour the second time it's called):
Okay this is one awful example, but here goes...
def f():
raise Exception("sparrow")
def g():
raise Exception("coconut")
def a():
f()
Suppose I did this:
try:
a()
except:
# attempt witty workaround
g()
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-4-c76b7509b315> in <module>()
3 except:
4 # attempt witty workaround
----> 5 g()
6
<ipython-input-2-e641f2f9a7dc> in g()
4
5 def g():
----> 6 raise Exception("coconut")
7
8
Exception: coconut
Well, the problem doesn't really lie with the coconut at all, but the sparrow:
try:
a()
except:
# attempt witty workaround
try:
g()
except:
# workaround failed, I want to rethrow the exception from calling a()
a() # ideally don't want to call a() again
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-4-e641f2f9a7dc> in <module>()
19 except:
20 # workaround failed, I want to rethrow the exception from calling a()
---> 21 a() # ideally don't want to call a() again
<ipython-input-3-e641f2f9a7dc> in a()
8
9 def a():
---> 10 f()
11
12
<ipython-input-1-e641f2f9a7dc> in f()
1 def f():
----> 2 raise Exception("sparrow")
3
4
5 def g():
Exception: sparrow
Is there a standard way to deal with this, or am I thinking about it completely wrong?
If you want to make it appear to the end user that you never called g(), then you need to store the traceback from the first error, call the second function and then throw the original with the original traceback. (otherwise, in Python2, bare raise re-raises the second exception rather than the first). The problem is that there is no 2/3 compatible way to raise with traceback, so you have to wrap the Python 2 version in an exec statement (since it's a SyntaxError in Python 3).
Here's a function that lets you do that (I added this to the pandas codebase recently):
import sys
if sys.version_info[0] >= 3:
def raise_with_traceback(exc, traceback=Ellipsis):
if traceback == Ellipsis:
_, _, traceback = sys.exc_info()
raise exc.with_traceback(traceback)
else:
# this version of raise is a syntax error in Python 3
exec("""
def raise_with_traceback(exc, traceback=Ellipsis):
if traceback == Ellipsis:
_, _, traceback = sys.exc_info()
raise exc, None, traceback
""")
raise_with_traceback.__doc__ = (
"""Raise exception with existing traceback.
If traceback is not passed, uses sys.exc_info() to get traceback."""
)
And then you can use it like this (I also changed the Exception types for clarity).
def f():
raise TypeError("sparrow")
def g():
raise ValueError("coconut")
def a():
f()
try:
a()
except TypeError as e:
import sys
# save the traceback from the original exception
_, _, tb = sys.exc_info()
try:
# attempt witty workaround
g()
except:
raise_with_traceback(e, tb)
And in Python 2, you only see a() and f():
Traceback (most recent call last):
File "test.py", line 40, in <module>
raise_with_traceback(e, tb)
File "test.py", line 31, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
But in Python 3, it still notes there was an additional exception too, because you are raising within its except clause [which flips the order of the errors and makes it much more confusing for the user]:
Traceback (most recent call last):
File "test.py", line 38, in <module>
g()
File "test.py", line 25, in g
raise ValueError("coconut")
ValueError: coconut
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 40, in <module>
raise_with_traceback(e, tb)
File "test.py", line 6, in raise_with_traceback
raise exc.with_traceback(traceback)
File "test.py", line 31, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
If you absolutely want it to look like the g() Exception never happened in both Python 2 and Python 3, you need to check that you are out of the except clause first:
try:
a()
except TypeError as e:
import sys
# save the traceback from the original exception
_, _, tb = sys.exc_info()
handled = False
try:
# attempt witty workaround
g()
handled = True
except:
pass
if not handled:
raise_with_traceback(e, tb)
Which gets you the following traceback in Python 2:
Traceback (most recent call last):
File "test.py", line 56, in <module>
raise_with_traceback(e, tb)
File "test.py", line 43, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
And this traceback in Python 3:
Traceback (most recent call last):
File "test.py", line 56, in <module>
raise_with_traceback(e, tb)
File "test.py", line 6, in raise_with_traceback
raise exc.with_traceback(traceback)
File "test.py", line 43, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
It does add an additional non-useful line of traceback that shows the raise exc.with_traceback(traceback) to the user, but it is relatively clean.
Here is something totally nutty that I wasn't sure would work, but it works in both python 2 and 3. (It does however, require the exception to be encapsulated into a function...)
def f():
print ("Fail!")
raise Exception("sparrow")
def g():
print ("Workaround fail.")
raise Exception("coconut")
def a():
f()
def tryhard():
ok = False
try:
a()
ok = True
finally:
if not ok:
try:
g()
return # "cancels" sparrow Exception by returning from finally
except:
pass
>>> tryhard()
Fail!
Workaround fail.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in tryhard
File "<stdin>", line 2, in a
File "<stdin>", line 3, in f
Exception: sparrow
Which is the correct exception and the right stack trace, and with no hackery.
>>> def g(): print "Worked around." # workaround is successful in this case
>>> tryhard()
Fail!
Worked around.
>>> def f(): print "Success!" # normal method works
>>> tryhard()
Success!
Ian Bicking has a nice primer on re-raising.
As a corollary, my rule is to only catch Exceptions that the code knows how to deal with. Very few methods actually hit this rule. For example, if I'm reading a file and an IOException is thrown, there is very little that method could reasonably do.
As a corollary to that, catching exceptions in "main" is reasonable if you can return to a good state and you don't just want to dump the user out; this only obtains in interactive programs.
The relevant section from the primer being the update:
try:
a()
except:
exc_info = sys.exc_info()
try:
g()
except:
# If this happens, it clobbers exc_info,
# which is why we had to save it above
import traceback
print >> sys.stderr, "Error in revert_stuff():"
# py3 print("Error in revert_stuff():", file=sys.stderr)
traceback.print_exc()
raise exc_info[0], exc_info[1], exc_info[2]
In python 3, the final raise could be written as:
ei = exc_info[1]
ei.filname = exc_info[0]
ei.__traceback__ = exc_info[2]
raise ei
In Python 3 (specifically tested on 3.3.2), this all works better, there's no need for saving sys.exc_info. Don't re-raise the original exception within the second exception handler. Just note that the 2nd attempt failed and raise the original in the scope of the original handler, like so:
#!python3
try:
a()
except Exception:
g_failed = False
try:
g()
except Exception:
g_failed = True
raise
Python 3 output correctly raising "sparrow" and showing traceback through a() and f():
Traceback (most recent call last):
File "x3.py", line 13, in <module>
a()
File "x3.py", line 10, in a
f()
File "x3.py", line 4, in f
raise Exception("sparrow")
Exception: sparrow
However, the same script on Python 2 incorrectly raising "coconut" and only showing g():
Traceback (most recent call last):
File "x3.py", line 17, in <module>
g()
File "x3.py", line 7, in g
raise Exception("coconut")
Exception: coconut
Here are the modifications to make Python 2 work correctly:
#!python2
import sys
try:
a()
except Exception:
exc = sys.exc_info()
try:
g()
except Exception:
raise exc[0], exc[1], exc[2] # Note doesn't care that it is nested.
Now Python 2 correctly shows "sparrow" and both a() and f() traceback:
Traceback (most recent call last):
File "x2.py", line 14, in <module>
a()
File "x2.py", line 11, in a
f()
File "x2.py", line 5, in f
raise Exception("sparrow")
Exception: sparrow
Capture the error in your except clause, then manually re-raise it later. Capture the traceback, and reprint it via the traceback module.
import sys
import traceback
def f():
raise Exception("sparrow")
def g():
raise Exception("coconut")
def a():
f()
try:
print "trying a"
a()
except Exception as e:
print sys.exc_info()
(_,_,tb) = sys.exc_info()
print "trying g"
try:
g()
except:
print "\n".join(traceback.format_tb(tb))
raise e
In Python 3, within a function, this can be done in a very slick way, following up the answer from #Mark Tolonen, who uses a boolean. You can't do this outside a function because there's no way to break out of the outer try statement: the function is needed for return.
#!python3
def f():
raise Exception("sparrow")
def g():
raise Exception("coconut")
def a():
f()
def h():
try:
a()
except:
try:
g()
return # Workaround succeeded!
except:
pass # Oh well, that didn't work.
raise # Re-raises *first* exception.
h()
This results in:
Traceback (most recent call last):
File "uc.py", line 23, in <module>
h()
File "uc.py", line 14, in h
a()
File "uc.py", line 10, in a
f()
File "uc.py", line 4, in f
raise Exception("sparrow")
Exception: sparrow
...and if instead g succeeds:
def g(): pass
...then it doesn't raise an exception.
try:
1/0 # will raise ZeroDivisionError
except Exception as first:
try:
x/1 # will raise NameError
except Exception as second:
raise first # will re-raise ZeroDivisionError

Categories