In my application I have a lot of conditions under which it is non-sensical to run the app further.
Currently I do something like this:
try:
some_fun()
except Exception as e:
print(f'Some short description: {str(e)}')
sys.exit(1)
There is a lot of boilerplate so I'd like to avoid it. I'm looking for something that would allow me to pass the string Some short description as a parameter and automate handling the exception of any kind.
You can register a custom exception hook:
import sys
def print_and_quit(type, value, traceback):
print("error:", value, file=sys.stderr)
sys.exit("something went wrong...")
def main():
sys.excepthook = print_and_quit
# your app entrypoint here...
if __name__ == "__main__":
main()
Note that the default except hook is already quite similar in behavior (printing a traceback and then exiting non-zero), but this allows to customize to do what you want instead. For example, to send an alert or log the exception to somewhere more useful for monitoring, suppress the traceback dump, exit differently for different types of errors, etc.
You could do this:
def handle(func, desc):
try:
func()
except Exception as e:
print(f'{desc}: {str(e)}')
sys.exit(1)
and use it as:
handle(lambda: 2/0, "some desc")
If you want args:
def handle(func, args, desc):
try:
func(*args)
except Exception as e:
print(f'{desc}: {str(e)}')
exit(1)
and to call this:
handle(lambda arg1: 2/0, ["abc"], "some desc")
which will call the lambda with "abc". Also, as #Talon said:
Instead of having the function take a list of arguments, you can star your args argument and make desc a keyword argument. – Talon
Which you could implement as:
def handle(func, desc, *args)
and call as:
handle(lambda arg1: 2/0, "some desc", "abc")
Though that, #wim's solution is better, as long as you don't need to handle specific exceptions.
You can try using :
import sys
import traceback
def my_exception(s, t):
#print(s) # the traceback is still available but not displayed
if t == "error in code X":
print("Something wrong but not critical\n")
return 0
elif t == "error in code Y":
print("Something critical, need to exit\n")
return 1
return 1
try:
val = int(".")
except:
pass
if my_exception(str(traceback.format_exc()), "error in code X"):
sys.exit(1)
print("I'm still here\n")
try:
val = 0/0
except:
pass
if my_exception(str(traceback.format_exc()), "error in code Y"):
sys.exit(1)
print("I'll never run")
Demo
Related
currently I have some code like this
try:
somecode
except Exception as Error:
fallbackcode
Now i want to add another fallback to the fallbackcode
Whats is the best practice to make a nested try/catch in python?
try:
somecode
except Exception as Error:
try:
fallbackcode
except Exception as Error:
nextfallbackcode
produces intendation errors
You should be able to do nested try/except blocks exactly how you have it implemented in your question.
Here's another example:
import os
def touch_file(file_to_touch):
"""
This method accepts a file path as a parameter and
returns true/false if it successfully 'touched' the file
:param '/path/to/file/'
:return: True/False
"""
file_touched = True
try:
os.utime(file_to_touch, None)
except EnvironmentError:
try:
open(file_to_touch, 'a').close()
except EnvironmentError:
file_touched = False
return file_touched
You can rewrite the logic using a loop provided all your callbacks and fallbacks have same API interface
for code in [somecode, fallbackcode, nextfallbackcode]:
try:
code(*args, **kwargs)
break
except Exception as Error:
continue
else:
raise HardException
This would be the preferred way instead of multiple level of nested exception blocks.
you can use functions to handle errors :
def func0():
try:
somecode
except:
other_func()
def func1():
try:
somecode
except:
func0()
You could have change it like this:
try:
try:
somecode
except Exception as Error:
fallbackcode
except Exception as Error:
nextfallbackcode
I'm practicing TDD in Python and came across a problem in testing whether an exception is raised.
Here is my test_phonebook.py with test_add_empty_name_raises_exception which fails.
import unittest
import phonebook
class Test(unittest.TestCase):
def test_add_empty_name_raises_exception(self):
self.assertRaises(ValueError, phonebook.add, "", "1111111111")
if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
unittest.main()
Below is my phonebook.py with the method add which adds the data into the dictionary.
import re
_phonebook = {}
file_name = "phonebook.txt"
def is_valid_name(name):
return re.search(r"([A-Z][a-z]*)([\\s\\\'-][A-Z][a-z]*)*", name) is not None
def is_valid_number(number):
return re.search(r"\+?[\d ]+$", number) is not None
def add(name, number):
try:
if is_valid_name(name) and is_valid_number(number):
_phonebook[name] = number
else:
raise ValueError("Invalid arguments.", name, number)
except ValueError as err:
print err.args
if __name__ == '__main__':
pass
My problem is that the test fails even though it is seen in the console log that there was a ValueError raised within the add method.
Finding files... done.
Importing test modules ... done.
('Invalid arguments.', '', '1111111111')
======================================================================
FAIL: test_add_empty_name_raises_exception (path.to.phonebook.test_phonebook.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "path\to\phonebook\test_phonebook.py", line 13, in test_add_empty_name_raises_exception
self.assertRaises(ValueError, phonebook.add, "", "1111111111")
AssertionError: ValueError not raised
----------------------------------------------------------------------
Ran 1 test in 0.002s
How do I solve this? I there something I forgot?
I also tried using the new format for handling exceptions in tests in Python 2.7 but it still hasn't caught the ValueError raising.
def test_add_empty_name_raises_exception(self):
with self.assertRaises(ValueError):
self.phonebook.add("", "1111111111)
I also changed the form of the test case into using lambdas but still no changes.
def test_add_empty_name_raises_exception(self):
self.assertRaises(ValueError, lambda: phonebook.add("", "1111111111"))
I also cleaned my directory and restarted Eclipse Luna and problem still persists.
Possible solution
I was reading the 8.Errors and Exceptions documentation and got to the "Raising Exceptions" part which states that:
If you need to determine whether an exception was raised but don’t intend to handle it,
a simpler form of the raise statement allows you to re-raise the exception:
I added this to the existing add method as such:
def add(name, number):
try:
if is_valid_name(name) and is_valid_number(number):
_phonebook[name] = number
print "Added %s:\t%s" % (name, number)
else:
raise ValueError("Invalid arguments.", name, number)
except ValueError as err:
print err.args
raise
Which caused the test case to pass.
Is this the correct way? To call raise again in the except block?
When you catch an exception (in your except ValueError as err: block), you prevent it from continuing back up the call stack to eventually terminate the program. Essentially, you're saying "I know how to handle this, so no need to panic anyone else."
Re-raising an exception is the proper thing to do if you caught the exception but didn't do so to actually fix anything, for instance, to log that it occurred. Typically, though, one catches an exception in order to correct it.
In your case, you're catching the exception almost immediately after you yourself raised it. Why not put your logging statement in the same else block as the raise? No need for a try: ... except: indent at all.
def add(name, number):
if is_valid_name(name) and is_valid_number(number):
_phonebook[name] = number
print "Added %s:\t%s" % (name, number)
else:
print "Invalid arguments.", name, number
raise ValueError("Invalid arguments.", name, number)
return
Hi im currently doing a program like this.
class MyError(Exception):
def __init__(self, text = "Correct")
self.text = text
def __str__(self):
return (self.kod)
class Atom(self):
.
.
.
try:
function()
else:
raise MyError("Incorrect use of function")
def main():
try:
a = Atom()
except:
# Here i want to print the error that was raised
What I think I understand is that the error is raised in an object created in Atom().
But I want to send it to my main program and do the print of the error MyError there.
Is it possible to do this and how should I write it so that the correct text of exception is printed since i will have several different error messages.
If i come to the except statement I would want to get the message "Incorrect use of function" printed.
It seems that you're pretty close:
class MyError(Exception):
def __init__(self, text = "Correct")
self.text = text
def __str__(self):
return (self.kod)
class Atom(self):
.
.
.
try:
function()
except: # try: ... else: raise ... seems a bit funky to me.
raise MyError("Incorrect use of function")
def main():
try:
a = Atom()
except Exception as err: # Possibly `except MyError as err` to be more specific
print err
The trick is that when you catch the error, you want to bind the exception instance to a name using the as clause. Then you can print it, look at it's attributes, re-raise or pretty much do anything you choose with it.
Please note that this code still isn't "clean". Generally, you want to limit exception handling as much as possible -- only catch exceptions that expect to see and that you know how to handle. Otherwise, you can sometimes mask hard to find bugs in your code. Because of this:
try:
do_something()
except:
...
is discouraged (it catches all sorts of things like KeyboardInterrupt and SystemExit) ... Instead:
try:
do_something()
except ExceptionIKnowHowToHandle:
...
is advised.
Firstly, never do a blank except. That will catch all errors, including things like KeyboardInterrupt - so you won't be able to ctrl-c out of your program. Here you should just catch MyError.
The except clause also allows you to assign the actual exception to a variable, which you can then print or do anything else with. So you can do:
try:
...
except MyError as e:
print e.text
So, let say I have 3 different calls called something, something1 and something2.
and right now, im calling it like
try:
something
something1
something2
except Keyerror as e:
print e
Note that in the above code, if something fails, something1 and something2 will not get executed and so on.
The wanted outcome is
try:
something
except KeyError as e:
print e
try:
something1
except KeyError as e:
print e
try:
something2
except KeyError as e:
print e
How can I achieve the above code without so many try except blocks.
EDIT:
So, the answer I chose as correct worked. But some of the others worked as well. I chose that because it was the simplist and I modified it a little.
Here is my solution based on the answer.
runs = [something, something1, something2]
for func in runs:
try:
func()
except Keyerror as e:
print e
You could try this, assuming you wrap things in functions:
for func in (something, something1, something2):
try:
func()
except Keyerror as e:
print e
Here's a little context manager I've used for similar situations:
from contextlib import contextmanager
#contextmanager
def ignoring(*exceptions):
try:
yield
except exceptions or Exception as e:
print e
with ignoring(KeyError):
something()
# you can also put it on the same line if it's just one statement
with ignoring(KeyError): something1()
with ignoring(KeyError): something2()
A Python 3 version could let you parameterize what to do when an exception occurs (the keyword-only arguments are needed here):
from contextlib import contextmanager
#contextmanager
def ignoring(*exceptions, action=print):
try:
yield
except exceptions or Exception as e:
callable(action) and action(e)
Then you could pass in some function other than print (such as a logger, assumed to be a function named log) or if you don't want anything, pass in None (since it checks to see if the action is callable):
with ignoring(KeyError, action=log): something()
I would go with something like this:
def safe_do(*statements):
for statement, args, kwargs in statements:
try:
statement(*args, **kwargs)
except KeyError as e:
print e
# usage:
safe_do(
(something1, [], {}),
(something2, [], {}),
)
But if you are expecting only one element to be missing in statements than why don't you if it?
if some_key1 in some_dict1:
something1
if some_key2 in some_dict2:
something2
much more readable and without any magic
Other possibility
def mydec(func):
def dec():
try:
func()
except KeyError as e:
print(e)
return dec
#mydec
def f1():
print('a')
#mydec
def f2():
print('b')
raise KeyError('Test')
f1()
f2()
This greatly depends on whether or not you're doing similar tasks, or very different tasks. For example, if your something lines are all very similar you could do the following:
def something(my_dict):
try:
d = my_dict['important_key'] # Just an example, since we
return d # don't know what you're really doing
except KeyError as e:
print e
something(dict1)
something(dict2)
something(dict3)
However, if your tasks are wildly different, this approach may not be applicable. To a certain degree you're asking "How do I write efficient code", and the answer to that depends on what code you're writing.
In python3, if you want to input a function with its args and kwargs, you can use the code below:
def safe_do(**statement):
try:
statement['func'](*statement['args'],**statement['kwargs'])
except Exception as e:
print(e)
print(statement['func'])
print(statement['args'])
print(statement['kwargs'])
def divide(a,b):
print(a/b)
safe_do(func=divide,args=[1,0],kwargs={})
In my colab notebook, I presented it.
I'm wondering how I can catch any raised object (i.e. a type that does not extend Exception), and still get a reference to it.
I came across the desire to do this when using Jython. When calling a Java method, if that method raises an exception, it will not extend Python's Exception class, so a block like this will not catch it:
try:
# some call to a java lib that raises an exception here
except Exception, e:
# will never be entered
I can do this, but then I have no access to the exception object that was raised.
try:
# some call to a java lib that raises an exception here
except:
# will enter here, but there's no reference to the exception that was raised
I can solve this by importing the Java exception type and catching it explicitly, but this makes it difficult/impossible to write generic exception handling wrappers/decorators.
Is there a way to catch some arbitrary exception and still get a reference to it in the except block?
I should note that I'm hoping for the exception handling decorator I am making to be usable with Python projects, not just with Jython projects. I'd like to avoid importing java.lang.Exception because that just makes it Jython-only. For example, I figure I can do something like this (but I haven't tried it), but I'd like to avoid it if I can.
try:
# some function that may be running jython and may raise a java exception
except (Exception, java.lang.Exception), e:
# I imagine this would work, but it makes the code jython-only
You can reference exceptions using the sys module. sys.exc_info is a tuple of the type, the instance and the traceback.
import sys
try:
# some call to a java lib that raises an exception here
except:
instance = sys.exc_info()[1]
FWIW, I have found that if you add this import to your Jython script:
from java.lang import Exception
and just use the conventional Python Exception handler:
except Exception, e:
it will catch both Python exceptions and Java exceptions
Just for anyone interested... I spent a bit of time testing stuff because I wanted to find out how to get a proper stack trace whether a Python Exception (BaseException in fact, which is the base class) or a java.lang.Throwable (java base class for Exception, Error, etc.) is thrown... this code illustrates how to catch all line number refs correctly.
import sys
import traceback
import java
print "hello world"
def custom_hook( type, exc, tb ):
if isinstance( sys.exc_info()[ 1 ], java.lang.Throwable ):
sys.stderr.write( "AS JAVA:\n" )
sys.exc_info()[ 1 ].printStackTrace() # java part
else:
sys.stderr.write( "NO JAVA TRACE:\n" )
sys.stderr.write( "AS PYTHON:\n" )
traceback.print_exc()
# useful for custom exception handling!
sys.excepthook = custom_hook
def handle_exc():
# generate either a java.lang.Throwable (uncomment the next line and comment out "a = 16 / 0"
# java.lang.String( None )
# OR... a python-style BaseException:
a = 16 / 0
class Task( java.lang.Runnable ):
def run( self ):
# NB the problem with all this stuff is that the Java stack trace shows
# a java.lang.Throwable occurring at the last line of this code block...
# print "lots of stuff first"
# print "lots 2"
# handle_exc()
# print "lots 3"
# print "lots of stuff after"
try:
print "lots of stuff first"
print "lots 2"
handle_exc()
print "lots 3"
print "lots of stuff after"
# NB do not catch both (Python) BaseException and java.lang.Throwable...
# except ( BaseException, java.lang.Throwable ), e:
# the above means that the line no. in handle_exc is not shown when a BaseException
# is thrown...
except java.lang.Throwable, t:
tb = sys.exc_info()[ 2 ]
sys.stderr.write( "java.lang.Throwable thrown at: %s\n" % tb.tb_lineno )
raise t
java.awt.EventQueue.invokeAndWait( Task() )
After this one might think of writing a decorator to precede def run( self ) and similar methods so that you don't have to write out this catch-the-Throwable try-except block each time... specifically:
def throw_trap( function ):
def wrapper(*args, **kvargs):
try:
return function( *args, **kvargs )
except java.lang.Throwable, t:
tb = sys.exc_info()[ 2 ]
while( tb ):
sys.stderr.write( "thrown at: %s\n" % tb.tb_lineno )
tb = tb.tb_next
raise t
return wrapper
def handle_exc():
java.lang.String( None )
# a = 16 / 0
class Task( java.lang.Runnable ):
#throw_trap
def run( self ):
print "lots of stuff first"
print "lots 2"
handle_exc()
print "lots 3"
print "lots of stuff after"
java.awt.EventQueue.invokeAndWait( Task() )