What will be the proper way to raise an exception and let the user know that it was raised from my_module, even if its a common python exception, like a ValueError:
#my_module.exceptions.py
class MyModuleError(Exception):
pass
#my_module.do_something.py
def do_something(*args):
try:
some logic here
except Exception as e:
# i dont know in advance what kinds of exceptions
# this code might throw
raise MyModuleError('An error ocurred %s' % (repr(e))
I would like the user to know that the exception was trown inside of my module, even tho it was a general python Exception.
Edit1: sorry, i should´ve explained better:
Even tho i dont know what kind of exceptions the code above might throw, i´d like the user to know that it was an error originated from my_module.
Is this even best practice?
How should i handle unexpected exceptions inside custom modules?
You can do the following:
#...
raise ValueError('This exception was raised from myModule')
#....
Related
I have code that raises exceptions from other exceptions so that we can see details about eveything that went wrong. In the example below we include information about what we are processing and what specific thing in the processing went wrong.
def process(thing):
try:
process_widgets(thing)
except Exception as e:
raise CouldNotProcess(thing) from e
def process_widgets(thing):
for widget in get_widgets(thing):
raise CouldNotProcessWidget(widget)
def do_processing():
for thing in things_to_process():
process(thing)
I am trying to change this so that process_widgets can raise a specific type of exception and do_processing can change its behaviour based on this exception. However the raise from in process is masking this exception which makes this impossible. Is there a good to let do_processing know about what went wrong in process_widgets while also doing raise from.
Ideas:
Python 3.11 has exception groups. So perhaps there is a way of adding exceptions to group and catching them with the likely confusing except* syntax.
There is a dirty trick where I do raise e from CouldNoPorcess(thing) to get both the helpful logging.
Apparently internally exception chaining works by adding __cause__ property (forming a linked list) so I could manually look through causes in the top most exception to manually implement behaviour like except* with exception groups.
def raised_from(e, type):
while e is not None:
if isinstance(e, Specific):
return True
e = e.__cause__
return False
...
try:
do_processing()
except CouldNotProcess as e:
if raised_from(e, CouldNotProcessWidget):
do_stuff()
You see this pattern quite a lot with status_codes from http.
I could use logging rather than adding information to exceptions. This hides information from the exception handling code, but works for logging. I think this is the work around that I'll use at the moment.
It's noticeable that the PEP says that exception chaining isn't quite designed for adding information to exceptions.
Update
python 3.11 has an add_note method and notes property which can be used to add information - which works for some use cases.
For this use case exception groups might be the way to go, though I am concerned that this might be a little confusing.
In Java, getting the message of an exception is as easy as always calling a certain method.
But in Python, it seems to be impossible. Sometimes it works by doing this:
try:
# Code
pass
except Exception as e:
print(e.message)
But sometimes capturing an exception like that ends up by raising another exception because the message attribute doesn't exist. Ironically sad. Trying to control a error produces another one...
Sometimes it works by doing this:
print(e.msg)
But sometimes it also raises missing attribute exception.
Sometimes this works as well:
print(str(e))
But sometimes it prints an empty string so it is simply useless.
I've even heard that it depends on the library you're using, on the concrete Exception implementation. That seems really stupid for me. How can I handle an error for printing what has happened if I never know what attributes does it have for retrieving the error message?
But sometimes it prints an empty string so it is simply useless.
Yeah, that's what happens when someone raises an exception without a message. Blame authors (of the lib you are using) for that.
Generally you can use repr which is supposed to be unambiguous and if not overriden contains at least information about the exception's type:
try:
0/0
except Exception as exc:
print(repr(exc))
raise
If you need whole traceback you can use
import traceback
try:
0/0
except Exception:
print(traceback.format_exc())
raise
There is a simple scenario that I seem to encounter quite often: I invoke a function that can raise any number of exceptions. I won't do anything different if it is one exception versus another, I just want to log the exception information and either re-raise the exception or indicate in some other way that something didn't go as planned (such as returning None), otherwise proceed normally. So I use some form of the exception handling shown below.
Please note:
Imagine his code is running in a daemon that processes messages, so it needs to keep running, even if one of the messages causes some kind of exception.
I am aware that there is a rule of thumb that it is not generally advisable to catch a generic Exception because that may hide specfic errors that should be handled differently. (This is true in other languages as well.) This case is different because I don't care what exception is raised, the handling is the same.
Is there a better way?
def my_func(p1):
retval = None
try:
valx = other_func1(p1)
except Exception as ex:
log.error('other_func1 failed. {}: {}'.format(type(ex).__name__, ex))
else:
retval = ...
return retval
Is there a better way?
Doubt it, Python has these built-in Base Exception Classes so creating something on your own is really just being redundant. If you handle everything in the same way, generalizing in your except with Exception is most likely the best way to tackle this.
Small caveat here: Exception isn't the most general you can get, from the documentation:
All built-in, non-system-exiting exceptions are derived from this class. All user-defined exceptions should also be derived from this class.
So, it won't catch all exceptions:
In [4]: try:
...: raise SystemExit
...: except Exception as b:
...: print("Catch All")
To exit: use 'exit', 'quit', or Ctrl-D.
An exception has occurred, use %tb to see the full traceback.
SystemExit
Which, do note, is of course something you should want. A SystemExit should exit. But, if some edge case requires it, to also catch system-exiting exceptions you can use BaseException which is as loose as you can get with exception matching:
In [2]: try:
...: raise SystemExit
...: except BaseException as b:
...: print("Catch All")
Catch All
Use it at your own discretion but, it probably makes zero sense to actually use it, and this case does not seem to require it. I just mentioned it because it is the most general you can get. I believe the way you have done it is more than sufficient.
That looks like a fine way to catch them if you're handling them all the same way. If you want to check what kind of exception was raised, you can use the built-in function type and compare the result to an exception class (for example, one from the list of built-in exception types):
try:
f()
except Exception as ex:
if type(ex)==ValueError:
handle_valueerror()
else:
handle_other_exception()
If you're handling them differently, use except <SpecificExceptionClass>. I'm not sure what I was thinking before.
I have a python module containing functions and a few classes. This module is basically used as a tool-set by several of my co-workers.
I want to set-up a sort of bug reporting system where anytime someone generates an exception that I don't handle, an email will be sent with information on the exception. This way I can continually improve the robustness of my code and the help-fullness of my own error messages. Is the best way to do this to just put a try/except block around the entire module?
There are several reasons I think your approach might not be the best.
Sometimes exceptions should be thrown. For example, if I pass some stupid argument to a function, it should complain by throwing an exception. You don't want to get an email every time someone passes a string instead of an integer, etc. do you?
Besides, wrapping the entire thing in a try...except won't work, as that will only be catching exceptions that would occur during the definition of the classes/functions (when your module is loaded/imported). For example,
# Your python library
try:
def foo():
raise Exception('foo exception')
return 42
except Exception as e:
print 'Handled: ', e
# A consumer of your library
foo()
The exception is still uncaught.
I guess you can make your own SelfMailingException and subclass it. Not that I would recommend this approach.
another option:
def raises(*exception_list):
def wrap(f):
def wrapped_f(*x, **y):
try:
f(*x, **y)
except Exception as e:
if not isinstance(e, tuple(exception_list)):
print('send mail')
# send mail
raise
return wrapped_f
return wrap
usage:
#raises(MyException)
def foo():
...
Then using try catch in python you can catch errors and assign them to a variable with the as keyword
try
do something..
except IOError as e:
do something with e..
However then trying to do the same thing without knowing the type of error python complains about the syntax.
try
do something..
except as e:
do something with e..
Is there any way to catch a default error and assign it to a variable?
Yes there is. All exceptions derive from the Exception class.
So you can do:
try:
doSomething()
except Exception as e:
doSomethingWithException(e)
It is a kind of catch-all line.
You can also use sys.exc_info(). This allows you to handle exceptions on Python 2.x and Python 3.x with the same code.
The conventional manner is:
try:
do_whatever()
except Exception as e:
handle_it()
Although it's ill-advised to catch such broad excepts - it's preferable to catch specific exceptions you know you can handle and let anything else propogate.
It's worth noting that KeyboardInterrupt and SystemExit inherit from BaseException and not Exception, so these wouldn't be caught were you expecting to cater for those, but that shouldn't be a problem as they should be handled at the top level anyway.