Python exception handling - line number - python

I'm using python to evaluate some measured data. Because of many possible results it is difficult to handle or possible combinations. Sometimes an error happens during the evaluation. It is usually an index error because I get out of range from measured data.
It is very difficult to find out on which place in code the problem happened. It would help a lot if I knew on which line the error was raised. If I use following code:
try:
result = evaluateData(data)
except Exception, err:
print ("Error: %s.\n" % str(err))
Unfortunately this only tells me that there is and index error. I would like to know more details about the exception (line in code, variable etc.) to find out what happened. Is it possible?
Thank you.

Solution, printing filename, linenumber, line itself and exception description:
import linecache
import sys
def PrintException():
exc_type, exc_obj, tb = sys.exc_info()
f = tb.tb_frame
lineno = tb.tb_lineno
filename = f.f_code.co_filename
linecache.checkcache(filename)
line = linecache.getline(filename, lineno, f.f_globals)
print 'EXCEPTION IN ({}, LINE {} "{}"): {}'.format(filename, lineno, line.strip(), exc_obj)
try:
print 1/0
except:
PrintException()
Output:
EXCEPTION IN (D:/Projects/delme3.py, LINE 15 "print 1/0"): integer division or modulo by zero

To simply get the line number you can use sys, if you would like to have more, try the traceback module.
import sys
try:
[][2]
except IndexError:
print("Error on line {}".format(sys.exc_info()[-1].tb_lineno))
prints:
Error on line 3
Example from the traceback module documentation:
import sys, traceback
def lumberjack():
bright_side_of_death()
def bright_side_of_death():
return tuple()[0]
try:
lumberjack()
except IndexError:
exc_type, exc_value, exc_traceback = sys.exc_info()
print "*** print_tb:"
traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
print "*** print_exception:"
traceback.print_exception(exc_type, exc_value, exc_traceback,
limit=2, file=sys.stdout)
print "*** print_exc:"
traceback.print_exc()
print "*** format_exc, first and last line:"
formatted_lines = traceback.format_exc().splitlines()
print formatted_lines[0]
print formatted_lines[-1]
print "*** format_exception:"
print repr(traceback.format_exception(exc_type, exc_value,
exc_traceback))
print "*** extract_tb:"
print repr(traceback.extract_tb(exc_traceback))
print "*** format_tb:"
print repr(traceback.format_tb(exc_traceback))
print "*** tb_lineno:", exc_traceback.tb_lineno

I use the traceback which is simple and robust:
import traceback
try:
raise ValueError()
except:
print(traceback.format_exc()) # or: traceback.print_exc()
Out:
Traceback (most recent call last):
File "catch.py", line 4, in <module>
raise ValueError()
ValueError

The simplest way is just to use:
import traceback
try:
<blah>
except IndexError:
traceback.print_exc()
or if using logging:
import logging
try:
<blah>
except IndexError as e:
logging.exception(e)

Gives you file, lineno, and exception for the last item in the call stack
from sys import exc_info
from traceback import format_exception
def print_exception():
etype, value, tb = exc_info()
info, error = format_exception(etype, value, tb)[-2:]
print(f'Exception in:\n{info}\n{error}')
try:
1 / 0
except:
print_exception()
prints
Exception in:
File "file.py", line 12, in <module>
1 / 0
ZeroDivisionError: division by zero

I would suggest using the python logging library, it has two useful methods that might help in this case.
logging.findCaller()
findCaller(stack_info=False) - Reports just the line number for the previous caller leading to the exception raised
findCaller(stack_info=True) - Reports the line number & stack for the previous caller leading to the exception raised
logging.logException()
Reports the line & stack within the try/except block that raised the exception
For more info checkout the api https://docs.python.org/3/library/logging.html

There are many answers already posted here that show how to get the line number, but it's worth noting that if you want variables containing the "raw data," so to speak, of the stack trace so that you can have more granular control of what you display or how you format it, using the traceback module you can step through the stack frame by frame and look at what's stored in the attributes of the frame summary objects. There are several simple and elegant ways to manipulate the frame summary objects directly. Let's say for example that you want the line number from the last frame in the stack (which tells you which line of code triggered the exception), here's how you could get it by accessing the relevant frame summary object:
Option 1:
import sys
import traceback
try:
# code that raises an exception
except Exception as exc:
exc_type, exc_value, exc_tb = sys.exc_info()
stack_summary = traceback.extract_tb(exc_tb)
end = stack_summary[-1] # or `stack_summary.pop(-1)` if you prefer
Option 2:
import sys
import traceback
try:
# code that raises an exception
except Exception as exc:
tbe = traceback.TracebackException(*sys.exc_info())
end = tbe.stack[-1] # or `tbe.stack.pop(-1)` if you prefer
In either of the above examples, end will be a frame summary object:
&gt&gt&gt type(end)
&ltclass &#39traceback.FrameSummary&#39&gt
which was in turn taken from a stack summary object:
&gt&gt&gt type(stack_summary) # from option 1
&ltclass &#39traceback.StackSummary&#39&gt
&gt&gt&gt type(tbe&#46stack) # from option 2
&ltclass &#39traceback.StackSummary&#39&gt
The stack summary object behaves like a list and you can iterate through all of the frame summary objects in it however you want in order to trace through the error. The frame summary object (end, in this example), contains the line number and everything else you need to locate where in the code the exception occurred:
>>> print(end.__doc__)
A single frame from a traceback.
- :attr:`filename` The filename for the frame.
- :attr:`lineno` The line within filename for the frame that was
active when the frame was captured.
- :attr:`name` The name of the function or method that was executing
when the frame was captured.
- :attr:`line` The text from the linecache module for the
of code that was running when the frame was captured.
- :attr:`locals` Either None if locals were not supplied, or a dict
mapping the name to the repr() of the variable.
And if you capture the exception object (either from except Exception as exc: syntax or from the second object returned by sys.exc_info()), you will then have everything you need to write your own highly customized error printing/logging function:
err_type = type(exc).__name__
err_msg = str(exc)
Putting it all together:
from datetime import datetime
import sys
import traceback
def print_custom_error_message():
exc_type, exc_value, exc_tb = sys.exc_info()
stack_summary = traceback.extract_tb(exc_tb)
end = stack_summary[-1]
err_type = type(exc_value).__name__
err_msg = str(exc_value)
date = datetime.strftime(datetime.now(), "%B %d, %Y at precisely %I:%M %p")
print(f"On {date}, a {err_type} occured in {end.filename} inside {end.name} on line {end.lineno} with the error message: {err_msg}.")
print(f"The following line of code is responsible: {end.line!r}")
print("Please make a note of it.")
def do_something_wrong():
try:
1/0
except Exception as exc:
print_custom_error_message()
if __name__ == "__main__":
do_something_wrong()
Let's run it!
user#some_machine:~$ python example.py
On August 25, 2022 at precisely 01:31 AM, a ZeroDivisionError occured in example.py inside do_something_wrong on line 21 with the error message: division by zero.
The following line of code is responsible: '1/0'
Please make a note of it.
At this point you can see how you could print this message for any place in the stack: end, beginning, anywhere in-between, or iterate through and print it for every frame in the stack.
Of course, the formatting functionality already provided by the traceback module covers most debugging use cases, but it's useful to know how to manipulate the traceback objects to extract the information you want.

I always use this snippet
import sys, os
try:
raise NotImplementedError("No error")
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print(exc_type, fname, exc_tb.tb_lineno)
for different views and possible issues you can refer When I catch an exception, how do I get the type, file, and line number?

All the solutions answer the OPs problem, however, if one is holding onto a specific error instance and the last traceback stack won't do it they will not suffice —a corner scenario example given below.
In this case, the magic attribute __traceback__ of a raised exception instance may be used. This is a system traceback object (exposed as types.TracebackType, see Types) not the module. This traceback instance behaves just one like would expect (but it is always nice to check):
from collections import deque
from typing import (List, )
errors: List[Exception] = deque([], 5)
def get_raised(error_cls: type, msg: str) -> Exception:
# for debugging, an exception instance that is not raised
# ``__traceback__`` has ``None``
try:
raise error_cls(msg)
except Exception as error:
return error
error = get_raised(NameError, 'foo')
errors.append(error)
error = get_raised(ValueError, 'bar')
errors.append(error)
try:
raise get_raised(TypeError, 'bar')
except Exception as error:
errors.append(error)
Now, I can check out the first error's details:
import types
traceback = errors[0].__traceback__ # inadvisable name due to module
line_no: int = traceback.tb_lineno
frame: types.FrameType = traceback.tb_frame
previous: Union[type(None), types.TracebackType] = traceback.tb_next
filename: str = frame.f_code.co_filename
The traceback's previous is None for the second error despite a preceding error as expected, but for the third wherein an error is raised twice it is not.
This below is just a test which makes no sense contextually. A case were it is useful if when an exception is raised in a view of a webapp (a 500 status kind of incident) that gets caught and stored for the admit to inspect akin to Setry.io (but for free). Here is a minimal example where the home page / will raise an error, that gets caught and the route errors will list them. This is using Pyramid in a very concentrated way (multifile is way better) with no logging or authentication and the error logging could be better for the admin to inspect similar to Sentry.io.
from pyramid.config import Configurator
from waitress import serve
from collections import deque
# just for typehinting:
from pyramid.request import Request
from pyramid.traversal import DefaultRootFactory
from pyramid.router import Router
import types
from typing import (List, )
def home_view(context: DefaultRootFactory, request: Request) -> dict:
raise NotImplementedError('I forgot to fill this')
return {'status': 'ok'} # never reached.
def caught_view(error: Exception, request: Request) -> dict:
"""
Exception above is just type hinting.
This is controlled by the context argument in
either the ``add_exception_view`` method of config,
or the ``exception_view_config`` decorator factory (callable class)
"""
# this below is a simplification as URLDecodeError is an attack (418)
request.response.status = 500
config.registry.settings['error_buffer'].append(error)
#logging.exception(error) # were it set up.
#slack_admin(format_error(error)) # ditto
return {'status': 'error', 'message': 'The server crashed!!'}
def format_error(error: Exception) -> str:
traceback = error.__traceback__ # inadvisable name due to module
frame: types.FrameType = traceback.tb_frame
return f'{type(error).__name__}: {error}' +\
f'at line {traceback.tb_lineno} in file {frame.f_code.co_filename}'
def error_view(context: DefaultRootFactory, request: Request) -> dict:
print(request.registry.settings['error_buffer'])
return {'status': 'ok',
'errors':list(map(format_error, request.registry.settings['error_buffer']))
}
with Configurator(settings=dict()) as config:
config.add_route('home', '/')
config.add_route('errors', '/errors')
config.add_view(home_view, route_name='home', renderer='json')
config.add_view(error_view, route_name='errors', renderer='json')
config.add_exception_view(caught_view, context=Exception, renderer='json')
config.registry.settings['error_buffer']: List[Exception] = deque([], 5)
# not in config.registry.settings, not JSON serialisable
# config.add_request_method
app : Router = config.make_wsgi_app()
port = 6969
serve(app, port=port)

Related

handling exception from python gammu library

I'm using python gammu library for sending sms. Sometimes something is wrong and I would like to handle exception. Descritpion of Exceptions is here: https://wammu.eu/docs/manual/python/exceptions.html#module-gammu.exception
I have a problem with getting and returning errors from such situations. I've printed:
print(sys.exc_info())
It has result:
(<class 'gammu.ERR_UNKNOWN'>, ERR_UNKNOWN({'Text': 'Nieznany błąd.', 'Where': 'SendSMS', 'Code': 27}), <traceback object at 0x740a6cd8>)
If i assign:
error_obj = sys.exc_info()
How can I get from it: Text, Code, and type ERROR(here is ERR_UKNOWN)?
I will grateful for help.
cls, exception, _ = sys.exc_info()
text = exception['Text'] # or exception.Text ?
code = exception['Code'] # or exception.Code ?
print(cls, text, code)
Also take a look at traceback module:
import traceback
try:
1/0
except ArithmeticError as e:
traceback.print_exc()
You should be able to use the args on the exception to get to the Text:
print(error_obj.args)
error_obj.args[0]['Text']

How to get the line number on which exception or error occurred in Python?

I have a Django ORM query like this:
try:
specialization_object = Specialization.objects.get(name="My Test Specialization")
except Exception as ex:
print(ex)
When there occurs an exception then it prints "Specialization matching query does not exist.", but it does not print the line number. How can I trace the line number on which the exception or error occurred?
try this:
import logging
logger = logging.getLogger(__name__)
try:
specialization_object = Specialization.objects.get(name="My Test Specialization")
except Exception as ex:
logger.info(ex, exc_info=True) # exc_info will add traceback
further reading see here
If you can't for any reason use logging, There's the standard package traceback, and you can do something like:
traceback.print_exc(file=sys.stdout)
I have figured out a simple solution just now:
import traceback
try:
specialization_object = Specialization.objects.get(name="My Test Specialization")
except Exception as ex:
print(traceback.format_exc())

Why can't I catch this python exception? Exception module/class doesn't match the catched module/class

I was writing some etcd modules for SaltStack and ran into this strange issue where it's somehow preventing me from catching an exception and I'm interested in how it's doing that. It seems specifically centered around urllib3.
A small script ( not salt ):
import etcd
c = etcd.Client('127.0.0.1', 4001)
print c.read('/test1', wait=True, timeout=2)
And when we run it:
[root#alpha utils]# /tmp/etcd_watch.py
Traceback (most recent call last):
File "/tmp/etcd_watch.py", line 5, in <module>
print c.read('/test1', wait=True, timeout=2)
File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read
timeout=timeout)
File "/usr/lib/python2.6/site-packages/etcd/client.py", line 788, in api_execute
cause=e
etcd.EtcdConnectionFailed: Connection to etcd failed due to ReadTimeoutError("HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out.",)
Ok, let's catch that bugger:
#!/usr/bin/python
import etcd
c = etcd.Client('127.0.0.1', 4001)
try:
print c.read('/test1', wait=True, timeout=2)
except etcd.EtcdConnectionFailed:
print 'connect failed'
Run it:
[root#alpha _modules]# /tmp/etcd_watch.py
connect failed
Looks good - it's all working python. So what's the issue? I have this in the salt etcd module:
[root#alpha _modules]# cat sjmh.py
import etcd
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
return c.read('/test1', wait=True, timeout=2)
except etcd.EtcdConnectionFailed:
return False
And when we run that:
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
The minion function caused an exception: Traceback (most recent call last):
File "/usr/lib/python2.6/site-packages/salt/minion.py", line 1173, in _thread_return
return_data = func(*args, **kwargs)
File "/var/cache/salt/minion/extmods/modules/sjmh.py", line 5, in test
c.read('/test1', wait=True, timeout=2)
File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read
timeout=timeout)
File "/usr/lib/python2.6/site-packages/etcd/client.py", line 769, in api_execute
_ = response.data
File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 150, in data
return self.read(cache_content=True)
File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 218, in read
raise ReadTimeoutError(self._pool, None, 'Read timed out.')
ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out.
Hrm, that's weird. etcd's read should have returned etcd.EtcdConnectionFailed. So, let's look at it further. Our module is now this:
import etcd
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
return c.read('/test1', wait=True, timeout=2)
except Exception as e:
return str(type(e))
And we get:
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
<class 'urllib3.exceptions.ReadTimeoutError'>
Ok, so we know that we can catch this thing. And we now know it threw a ReadTimeoutError, so let's catch that. The newest version of our module:
import etcd
import urllib3.exceptions
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
c.read('/test1', wait=True, timeout=2)
except urllib3.exceptions.ReadTimeoutError as e:
return 'caught ya!'
except Exception as e:
return str(type(e))
And our test..
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
<class 'urllib3.exceptions.ReadTimeoutError'>
Er, wait, what? Why didn't we catch that? Exceptions work, right.. ?
How about if we try and catch the base class from urllib3..
[root#alpha _modules]# cat sjmh.py
import etcd
import urllib3.exceptions
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
c.read('/test1', wait=True, timeout=2)
except urllib3.exceptions.HTTPError:
return 'got you this time!'
Hope and pray..
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
The minion function caused an exception: Traceback (most recent call last):
File "/usr/lib/python2.6/site-packages/salt/minion.py", line 1173, in _thread_return
return_data = func(*args, **kwargs)
File "/var/cache/salt/minion/extmods/modules/sjmh.py", line 7, in test
c.read('/test1', wait=True, timeout=2)
File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read
timeout=timeout)
File "/usr/lib/python2.6/site-packages/etcd/client.py", line 769, in api_execute
_ = response.data
File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 150, in data
return self.read(cache_content=True)
File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 218, in read
raise ReadTimeoutError(self._pool, None, 'Read timed out.')
ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out.
BLAST YE! Ok, let's try a different method that returns a different etcd Exception. Our module now looks like this:
import etcd
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
c.delete('/')
except etcd.EtcdRootReadOnly:
return 'got you this time!'
And our run:
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
got you this time!
As a final test, I made this module, which I can run either from straight python, or as a salt module..
import etcd
import urllib3
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
c.read('/test1', wait=True, timeout=2)
except urllib3.exceptions.ReadTimeoutError:
return 'got you this time!'
except etcd.EtcdConnectionFailed:
return 'cant get away from me!'
except etcd.EtcdException:
return 'oh no you dont'
except urllib3.exceptions.HTTPError:
return 'get back here!'
except Exception as e:
return 'HOW DID YOU GET HERE? {0}'.format(type(e))
if __name__ == "__main__":
print test()
Through python:
[root#alpha _modules]# python ./sjmh.py
cant get away from me!
Through salt:
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
HOW DID YOU GET HERE? <class 'urllib3.exceptions.ReadTimeoutError'>
So, we can catch exceptions from etcd that it throws. But, while we normally are able to catch the urllib3 ReadTimeoutError when we run python-etcd by its lonesome, when I run it through salt, nothing seems to be able to catch that urllib3 exception, except a blanket 'Exception' clause.
I can do that, but I'm really curious as to what the heck salt is doing that's making it so that an exception is uncatchable. I've never seen this before when working with python, so I'd be curious as to how it's happening and how I can work around it.
Edit:
So I was finally able to catch it.
import etcd
import urllib3.exceptions
from urllib3.exceptions import ReadTimeoutError
def test():
c = etcd.Client('127.0.0.1', 4001)
try:
c.read('/test1', wait=True, timeout=2)
except urllib3.exceptions.ReadTimeoutError:
return 'caught 1'
except urllib3.exceptions.HTTPError:
return 'caught 2'
except ReadTimeoutError:
return 'caught 3'
except etcd.EtcdConnectionFailed as ex:
return 'cant get away from me!'
except Exception as ex:
return 'HOW DID YOU GET HERE? {0}'.format(type(ex))
if __name__ == "__main__":
print test()
And when run:
[root#alpha _modules]# salt 'alpha' sjmh.test
alpha:
caught 3
It still doesn't make sense though. From what I know of exceptions, the return should be 'caught 1'. Why should I have to import the name of the exception directly, rather than just using full class name?
MORE EDITS!
So, adding the comparison between the two classes produces 'False' - which is sorta obvious, because the except clause wasn't working, so those couldn't be the same.
I added the following to the script, right before I call the c.read().
log.debug(urllib3.exceptions.ReadTimeoutError.__module__)
log.debug(ReadTimeoutError.__module__)
And now I get this in the log:
[DEBUG ] requests.packages.urllib3.exceptions
[DEBUG ] urllib3.exceptions
So, that appears to be the reason that is getting caught the way it is. This is also reproducible by just downloading the etcd and requests library and doing something like this:
#!/usr/bin/python
#import requests
import etcd
c = etcd.Client('127.0.0.1', 4001)
c.read("/blah", wait=True, timeout=2)
You'll end up getting the 'right' exception raised - etcd.EtcdConnectionFailed. However, uncomment 'requests' and you'll end up with urllib3.exceptions.ReadTimeoutError, because etcd now no longer catches the exception.
So it appears that when requests is imported, it rewrites the urllib3 exceptions, and any other module that is trying to catch those, fails. Also, it appears that newer versions of requests do not have this issue.
My answer below is a little of speculation, because I cannot prove it on practice with these exact libraries (to start with I cannot reproduce your error as it also depends on libraries versions and how they are installed), but nevertheless shows one of the possible ways of this happening:
The very last example gives a good clue: the point is indeed that at different moments in the time of the program execution, name urllib3.exceptions.ReadTimeoutError may refer to different classes. ReadTimeoutError is, just like for every other module in Python, is simply a name in the urllib3.exceptions namespace, and can be reassigned (but it doesn't mean it is a good idea to do so).
When referring to this name by its fully-qualified "path" - we are guaranteed to refer to the actual state of it by the time we refer to it. However, when we first import it like from urllib3.exceptions import ReadTimeoutError - it brings name ReadTimeoutError into the namespace which does the import, and this name is bound to the value of urllib3.exceptions.ReadTimeoutError by the time of this import. Now, if some other code reassigns later the value for urllib3.exceptions.ReadTimeoutError - the two (its "current"/"latest" value and the previously imported one) might be actually different - so technically you might end up having two different classes. Now, which exception class will be actually raised - this depends on how the code which raises the error uses it: if they previously imported the ReadTimeoutError into their namespace - then this one (the "original") will be raised.
To verify if this is the case you might add the following to the except ReadTimeoutError block:
print(urllib3.exceptions.ReadTimeoutError == ReadTimeoutError)
If this prints False - it proves that by the time the exception is raised, the two "references" refer to different classes indeed.
A simplified example of a poor implementation which can yield similar result:
File api.py (properly designed and exists happily by itself):
class MyApiException(Exception):
pass
def foo():
raise MyApiException('BOOM!')
File apibreaker.py (the one to blame):
import api
class MyVeryOwnException(Exception):
# note, this doesn't extend MyApiException,
# but creates a new "branch" in the hierarhcy
pass
# DON'T DO THIS AT HOME!
api.MyApiException = MyVeryOwnException
File apiuser.py:
import api
from api import MyApiException, foo
import apibreaker
if __name__ == '__main__':
try:
foo()
except MyApiException:
print("Caught exception of an original class")
except api.MyApiException:
print("Caught exception of a reassigned class")
When executed:
$ python apiuser.py
Caught exception of a reassigned class
If you remove the line import apibreaker - clearly then all goes back to their places as it should be.
This is a very simplified example, yet illustrative enough to show that when a class is defined in some module - newly created type (object representing new class itself) is "added" under its declared class name to the module's namespace. As with any other variable - its value can be technically modified. Same thing happens to functions.

python3 re-raising an exception with custom attribute?

here's my code from python2 which needs to be ported:
try:
do_something_with_file(filename)
except:
exc_type, exc_inst, tb = sys.exc_info()
exc_inst.filename = filename
raise exc_type, exc_inst, tb
with above code, I can get the whole exception with the problematic input file by checking whether an exception has 'filename' attribute.
however python3's raise has been changed. this is what 2to3 gave me for above code:
except Exception as e:
et, ei, tb = sys.exc_info()
e.filename = filename
raise et(e).with_traceback(tb)
which gives me another error and I don't think filename attribute is preserved:
in __call__
raise et(e).with_traceback(tb)
TypeError: function takes exactly 5 arguments (1 given)
What I just want is passing exceptions transparently with some information to track the input file. I miss python2's raise [exception_type[,exception_instance[,traceback]]] - How can I do this in python3?
You can set the __traceback__ attribute:
except Exception as e:
et, ei, tb = sys.exc_info()
ei.filename = filename
ei.__traceback__ = tb
raise ei
or call .with_traceback() directly on the old instance:
except Exception as e:
et, ei, tb = sys.exc_info()
ei.filename = filename
raise ei.with_traceback(tb)
However, the traceback is already automatically attached, there is no need to re-attach it, really.
See the raise statement documentation:
A traceback object is normally created automatically when an exception is raised and attached to it as the __traceback__ attribute, which is writable.
In this specific case, perhaps you wanted a different exception instead, with context?
class FilenameException(Exception):
filename = None
def __init__(self, filename):
super().__init__(filename)
self.filename = filename
try:
something(filename)
except Exception as e:
raise FilenameException(filename) from e
This would create a chained exception, where both exceptions would be printed if uncaught, and the original exception is available as newexception.__context__.
You don't need to do anything; in Python 3 re-raised exceptions automatically have a full traceback from where they were originally raised.
try:
do_something_with_file(filename)
except Exception as exc_inst:
exc_inst.filename = filename
raise exc_inst
This works because PyErr_NormalizeException sets the __traceback__ attribute appropriately on catching an exception in an except statement; see http://www.python.org/dev/peps/pep-3134/.

Raise two errors at the same time

Is there any way to raise two errors at the same time by using try and except?
For example, ValueError and KeyError.
How do I do that?
The question asks how to RAISE multiple errors not catch multiple errors.
Strictly speaking you can't raise multiple exceptions but you could raise an object that contains multiple exceptions.
raise Exception(
[
Exception("bad"),
Exception("really bad"),
Exception("really really bad"),
]
)
Question: Why would you ever want to do this?
Answer: In a loop when you want to raise an error but process the loop to completion.
For example when unit-testing with unittest2 you might want to raise an exception and keep processing then raise all of the errors at the end. This way you can see all of the errors at once.
def test_me(self):
errors = []
for modulation in self.modulations:
logging.info('Testing modulation = {modulation}'.format(**locals()))
self.digitalModulation().set('value', modulation)
reply = self.getReply()
try:
self._test_nodeValue(reply, self.digitalModulation())
except Exception as e:
errors.append(e)
if errors:
raise Exception(errors)
Python 3.11
Starting with 3.11 you can use ExceptionGroup to raise multiple exceptions.
raise ExceptionGroup("this was bad",
[
Exception("bad"),
Exception("really bad"),
Exception("really really bad"),
]
)
You could raise an error which inherits from both ValueError and KeyError. It would get caught by a catch block for either.
class MyError(ValueError, KeyError):
...
Yes, you can handle more than one error, either using
try:
# your code here
except (ValueError, KeyError) as e:
# catch it, the exception is accessable via the variable e
Or, directly add two "ways" of handling different errors:
try:
# your code here
except ValueError as e:
# catch it, the exception is accessable via the variable e
except KeyError as e:
# catch it, the exception is accessable via the variable e
You may also leave out the "e" variable.
Checkout the documentation: http://docs.python.org/tutorial/errors.html#handling-exceptions
The solution from #shrewmouse still requires to choose an exception class to wrap the caught exceptions.
Following solution uses Exception Chaining via finally to execute code after one exception occurs
We don't need to know beforehand, what exceptions occur
Note that only the first exception that occurs can be detected from the caller via except
if this is a problem, use #Collin's solution above to inherit from all collected exceptions
You'll see the exceptions separated by:
"During handling of the above exception, another exception occurred:"
def raise_multiple(errors):
if not errors: # list emptied, recursion ends
return
try:
raise errors.pop() # pop removes list entries
finally:
raise_multiple(errors) # recursion
If you have a task that needs to be done for each element of a list, you don't need to collect the Exceptions beforehand. Here's an example for multiple file deletion with multiple error reporting:
def delete_multiple(files):
if not files:
return
try:
os.remove(files.pop())
finally:
delete_multiple(files)
PS:
Tested with Python 3.8.5
To print full traceback per exception have a look at traceback.print_exc
The original question is answered since years. But as this page is the top search result for "python raise multiple" I share my approach to fill an (IMHO relevant) gap in the solution spectrum.
try :
pass
except (ValueError,KeyError):
pass
read more about Handling exceptions
You can Raise more than one exception like this:
try:
i = 0
j = 1 / i
except ZeroDivisionError:
try:
i = 'j'
j = 4 + i
except TypeError:
raise ValueError
NOTE: it may be that only the ValueError is raised but this error message seems right:
Traceback (most recent call last):
File "<pyshell#9>", line 3, in <module>
j = 1 / i
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#9>", line 7, in <module>
j = 4 + i
TypeError: unsupported operand type(s) for +: 'int' and 'str'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#9>", line 9, in <module>
raise ValueError
ValueError

Categories