Catching boto3 ClientError subclass - python

With code like the snippet below, we can catch AWS exceptions:
from aws_utils import make_session
session = make_session()
cf = session.resource("iam")
role = cf.Role("foo")
try:
role.load()
except Exception as e:
print(type(e))
raise e
The returned error is of type botocore.errorfactory.NoSuchEntityException. However, when I try to import this exception, I get this:
>>> import botocore.errorfactory.NoSuchEntityException
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named NoSuchEntityException
The best method I could find of catching this specific error is:
from botocore.exceptions import ClientError
session = make_session()
cf = session.resource("iam")
role = cf.Role("foo")
try:
role.load()
except ClientError as e:
if e.response["Error"]["Code"] == "NoSuchEntity":
# ignore the target exception
pass
else:
# this is not the exception we are looking for
raise e
But this seems very "hackish". Is there a way to directly import and catch specific subclasses of ClientError in boto3?
EDIT: Note that if you catch errors in the second way and print the type, it will be ClientError.

If you're using the client you can catch the exceptions like this:
import boto3
def exists(role_name):
client = boto3.client('iam')
try:
client.get_role(RoleName='foo')
return True
except client.exceptions.NoSuchEntityException:
return False

If you're using the resource you can catch the exceptions like this:
cf = session.resource("iam")
role = cf.Role("foo")
try:
role.load()
except cf.meta.client.exceptions.NoSuchEntityException:
# ignore the target exception
pass
This combines the earlier answer with the simple trick of using .meta.client to get from the higher-level resource to the lower-level client (source: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#creating-clients).

try:
something
except client.exceptions.NoSuchEntityException:
something
This worked for me

Related

Download CSV files from S3 using Python

import os
import boto3
import json
from datetime import datetime,timedelta
s3 = boto3.client('s3', aws_access_key_id="<access_key>",
aws_secret_access_key="<secret_key>")
##my_bucket = s3.Bucket('grn-amazon')
filename_withdate=(datetime.now()-timedelta(days=6)).strftime ("%d%b%Y")
filename_withdate = filename_withdate+'_Consolidated.csv'
Source_filename = filename_withdate
dest_filename = filename_withdate
try:
## s3.download_file('grn-amazon',complt_filename,'01FEB2020_Consolidated.csv')
s3.download_file('grn-amazon',Source_filename,dest_filename)
print("Download Completed")
except botocore.exception.ClientErrors as e:
if e.response['Error']['Code'] == '404':
print('The Object does not exists!!')
else:
raise
Getting below error after running above code. Kindly help on this.. Passing Source and Destination file name as parameter in S3 attributes...
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Cloudtail\CT\SQL Scripts\python\GRN_S3_dwnld.py", line 17, in
except botocore.exception.ClientErrors as e:
NameError: name 'botocore' is not defined
Figured out an error, its not boto3 error. Filename in Capital letter like 01FEB2020, my source file is taking like 01Feb2020.
Change above date with upper() function
filename_withdate=((datetime.now()-timedelta(days=6)).strftime ("%d%b%Y").upper())
Thanks all to your valuable time and responses.
Add this to the top of your Python program:
from botocore.exceptions import ClientError
Change
except botocore.exception.ClientErrors as e:
into:
except botocore.exception.ClientError as e:

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())

Script raises HTTPError, but cannot catch

I have an error which I have a little difficulty understanding. I have a script which uses biopython to query a database. Sometimes, biopython can't find what we're looking for, and an HTTPError is thrown. I cannot, however catch the HTTPError, as I get the following error message:
HTTPError: HTTP Error 404: Not Found
During handling of the above exception, another exception occurred:
NameError Traceback (most recent call
last) in ()
51 UniProt = text[index+9:index+15]
52 uniprot_IDs[bigg_ID] = UniProt
---> 53 except HTTPError:
54 if err.code == '404':
55 uniprot_IDs[biGG_ID] = None
NameError: name 'HTTPError' is not defined
How can an error which is not defined be thrown in the first place? What am I missing?
This is the relevant code:
from Bio.KEGG import REST, Enzyme
from DataTreatment import openJson, write
...
try:
ec_number = some_string
text = REST.kegg_get('ec:'+ec_number).read()
...
except HTTPError:
if err.code == '404':
a_dict[a_key] = None
You need to import the HTTPError class. If you already imported, then make sure you got the right one. You can try to catch with a generic Exception and use type(ex) to find out which it is and import the correct type.
You need to import the HTTPError-class, try this;
In the top of your code, add
from urllib.error import HTTPError
Source: Entrez._HTTPError vs. Entrez.HTTPError (via Entrez.efetch)

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.

Python exception handling - line number

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)

Categories