KeyError exception object contains args attribute. This is a list and it contains a key name which user tries to access within a dictionary.
Is it possible to figure out dictionary name which does not contain that key and which caused an exception while trying to access the key within it?
Example
data = {"my_key": "my_value"}
try:
data["unknown_key"] except KeyError as e:
print("key name: ", e.args[0])
print("dictionary name: ", e.???) # Here I would need to know the the name of a variable which stores the dictionary is "data"
You can kinda hack this with Python 3.11+, since the traceback contains fine-grained information about where the error happened.
import ast
import linecache
import traceback
data = {"my_key": "my_value"}
flurk = data
try:
data["flep"] = data["my_key"] + flurk["unknown_key"]
except KeyError as e:
# Find the last frame where the exception occurred, formatted as a FrameSummary
err_frame = traceback.TracebackException.from_exception(e).stack[-1]
if err_frame.lineno == getattr(err_frame, "end_lineno", -1): # If we can reliably find the line,
# ... read the line,
line = linecache.getline(err_frame.filename, err_frame.lineno)
# find the "marked segment" in it,
fragment = line[err_frame.colno:err_frame.end_colno]
# ... and parse it as an expression.
expr: ast.Expression = ast.parse(fragment, mode='eval')
# Check we're dealing with a subscript (index) node...
assert isinstance(expr.body, ast.Subscript)
# ... and extract the main parts of the expression.
subscriptee = ast.unparse(expr.body.value)
subscript = ast.unparse(expr.body.slice)
else:
subscriptee = None # No idea
subscript = e.args[0] # Just use the exception message
raise RuntimeError(f"KeyError with {subscriptee=!r}, {subscript=!r}") from e
prints out
Traceback (most recent call last):
File "scratch_679.py", line 8, in <module>
data["flep"] = data["my_key"] + flurk["unknown_key"]
~~~~~^^^^^^^^^^^^^^^
KeyError: 'unknown_key'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "scratch_679.py", line 27, in <module>
raise RuntimeError(f"KeyError with {subscriptee=!r}, {subscript=!r}") from e
RuntimeError: KeyError with subscriptee='flurk', subscript="'unknown_key'"
so you can see the subscriptee name is flurk.
Related
I want to print an error's line number and error message in a nicely displayed way. The follow is my code, which uses linecache:
import linecache
def func():
if xx == 1:
print('ok')
try:
func()
except:
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_('ERROR - (LINE {} "{}"): {}'.format(lineno, line.strip(), exc_obj))
However, this only gives where the func() is called:
ERROR - (LINE 8 ""): name 'xx' is not defined
Is there a way to print the line number where the error actually occured, which should be Line 4? Or even better, can I print Line 8 and then trace back to line 4? For example, if I do not use try - except, the code:
def func():
if xx == 1:
print('ok')
func()
will give me the following error message, which is much better to locate the error:
File "<input>", line 5, in <module>
File "<input>", line 2, in func
NameError: name 'xx' is not defined. Did you mean: 'xxx'?
You can use traceback and sys modules to get advanced traceback output like you are wishing for.
Here is an example:
import traceback
import sys
def func():
zeroDivide = 1 / 0
try:
func()
except Exception:
print(traceback.format_exc()) # This line is for getting traceback.
print(sys.exc_info()[2]) # This line is getting for the error type.
Output will be:
Traceback (most recent call last):
File "b:\abc\1234\pppp\main.py", line 10, in <module>
func()
File "b:\abc\1234\pppp\main.py", line 7, in func
zeroDivide = 1 / 0
ZeroDivisionError: division by zero
You can use the traceback module to get the line number of the error,
import traceback
def function():
try:
# code
except:
tb_list = traceback.extract_tb(sys.exc_info()[2])
line_number = tb_list[-1][1]
print("An error occurred on line:", line_number)
You can use the traceback.extract_tb() function. This function returns a list of traceback objects, each of which contain information about the stack trace. The last element of this list, tb_list[-1], holds information about the line where the exception occurred. To access the line number, you can use the second element of this tuple, tb_list[-1][1]. This value can then be printed using the print() function.
To get the line number as an int you can get the traceback as a list from traceback.extract_tb(). Looking at the last item gives you the line where the exception was raised:
#soPrintLineOfError2
import sys
import traceback
def func():
if xx == 1:
print('ok')
try:
func()
except Exception as e:
tb = sys.exc_info()[2]
ss = traceback.extract_tb(tb)
ss1 = ss[-1]
print(ss1.line)
print(ss1.lineno)
Output:
if xx == 1:
6
I am trying to search within the EU parliment votes' description.
They are standard xml files. So far I noticed 2 versions of the votes' result: where the description is text and where it is an url.
Fails at this file: https://www.europarl.europa.eu/doceo/document/PV-9-2022-09-12-RCV_FR.xml
Works fine on the following file: https://www.europarl.europa.eu/doceo/document/PV-9-2022-07-06-RCV_FR.xml
My problem is not the failure itself (that's why I added the try clause) but that code execution doesn't jump to the except path, just exits here with this: string indices must be integers
Please help, why isn't the error properly handled?
for currentfile in f:
mytree = ET.parse(xmlfiles + "\\" + currentfile)
myroot = mytree.getroot()
dixml = etree_to_dict(myroot)
for votes in dixml['PV.RollCallVoteResults']['RollCallVote.Result']:
try:
title = votes['RollCallVote.Description.Text'] #fails here
titletype = type(title)
if titletype == dict:
title=title['#text']
except :
title = votes['RollCallVote.Description.Text']['a']['#text']
try:
ltitle = title.lower()
except :
print(type(title))
if stringtosearch in ltitle:
print(title,currentfile)
edit:
full trace:
Message=string indices must be integers
Source=...debug.py
StackTrace:
File "...debug.py", line 44, in <module>
title = votes['RollCallVote.Description.Text']
During handling of the above exception, another exception occurred:
File "...debug.py", line 44, in <module>
title = votes['RollCallVote.Description.Text']
File "...debug.py", line 49, in <module> (Current frame)
title = votes['RollCallVote.Description.Text']['a']['#text']
This would happen if there is a new exception raised within your catch block, I would presume specifically the votes object at some point of you accessing it, raises this exception.
Try printing out votes and then each time you access it by indexing print it out you'll see to which extent you can continue accessing it via string index ( votes['string'] ).
I am assigning the stack trace variable traceback.format_exc() to a list as below ,strange thing I notice is after appending ,all the single quotes(') get escaped (\') as can be seen from the output below.
I looked on google #https://github.com/behave/behave/issues/336 and tried to assign (traceback.format_exc(), sys.getfilesystemencoding() which didn't work either,am very curious why is this happening and how to fix this?
import traceback
clonedRadarsdetailslist = []
clonedRadardetails = {}
try:
#raise
(updateproblemoutput,updateproblempassfail) = r.UpdateProblem(problemID=newRadarID, componentName=componentName, componentVersion=componentVersion,assigneeID=assignee,state=state,substate=substate,milestone=milestone, category=category,priority=priority,resolution=re_solution )
except:
clonedRadardetails['updatedFailedReason'] = traceback.format_exc()
clonedRadarsdetailslist.append(clonedRadardetails)
print clonedRadarsdetailslist
OUTPUT:-
['{\'clonedRadar\': 40171867, \'clonedStatus\': \'PASS\', \'clonedRadarFinalStatus\': \'PASS\', \'updatedFailedReason\': \'Traceback (most recent call last):\\n File "./cloneradar.py", line 174, in clone\\n (updatetitleoutput,updatetitlepassfail) = r.UpdateProble(problemID=newRadarID,title=title )\\nAttributeError: \\\'RadarWS\\\' object has no attribute \\\'UpdateProble\\\'\\n\', \'clonedRadarFinalStatusReason\': \'N/A\', \'updateStatus\': \'FAIL\', \'clonedStatusfailReason\': \'N/A\'}', '{\'clonedRadar\': 40171867, \'clonedStatus\': \'PASS\', \'clonedRadarFinalStatus\': \'PASS\', \'updatedFailedReason\': \'Traceback (most recent call last):\\n File "./cloneradar.py", line 174, in clone\\n (updatetitleoutput,updatetitlepassfail) = r.UpdateProble(problemID=newRadarID,title=title )\\nAttributeError: \\\'RadarWS\\\' object has no attribute \\\'UpdateProble\\\'\\n\', \'clonedRadarFinalStatusReason\': \'N/A\', \'updateStatus\': \'FAIL\', \'clonedStatusfailReason\': \'N/A\'}']
The below function parses the cisco command output,stores the output in dictionary and returns the value for a given key. This function works as expected when the dictionary contains the output. However, if the command returns no output at all the length of dictionary is 0 and the function returns a key error . I have used exception KeyError: But this doesn't seem to work.
from qa.ssh import Ssh
import re
class crypto:
def __init__(self, username, ip, password, machinetype):
self.user_name = username
self.ip_address = ip
self.pass_word = password
self.machine_type = machinetype
self.router_ssh = Ssh(ip=self.ip_address,
user=self.user_name,
password=self.pass_word,
machine_type=self.machine_type
)
def session_status(self, interface):
command = 'show crypto session interface '+interface
result = self.router_ssh.cmd(command)
try:
resultDict = dict(map(str.strip, line.split(':', 1))
for line in result.split('\n') if ':' in line)
return resultDict
except KeyError:
return False
test script :
obj = crypto('uname', 'ipaddr', 'password', 'router')
out = obj.session_status('tunnel0')
status = out['Peer']
print(status)
Error
Traceback (most recent call last):
File "./test_parser.py", line 16, in <module>
status = out['Peer']
KeyError: 'Peer'
The KeyError did not happend in the function session_status,it is happend in your script at status = out['Peer'].So your try and except in session_status will not work.you should make a try and except for status = out['Peer']:
try:
status = out['Peer']
except KeyError:
print 'no Peer'
or :
status = out.get('Peer', None)
Your exception is not in the right place. As you said you just return an empty dictionary with your function. The exception is trying to lookup the key on empty dictionary object that is returned status = outertunnel['Peer']. It might be easier to check it with the dict get function. status = outertunnel.get('Peer',False) or improve the test within the function session_status, like testing the length to decide what to return False if len(resultDict) == 0
This explains the problem you're seeing.
The exception happens when you reference out['Peer'] because out is an empty dict. To see where the KeyError exception can come into play, this is how it operates on an empty dict:
out = {}
status = out['Peer']
Throws the error you're seeing. The following shows how to deal with an unfound key in out:
out = {}
try:
status = out['Peer']
except KeyError:
status = False
print('The key you asked for is not here status has been set to False')
Even if the returned object was False, out['Peer'] still fails:
>>> out = False
>>> out['Peer']
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
out['Peer']
TypeError: 'bool' object is not subscriptable
I'm not sure how you should proceed, but dealing with the result of session_status not having the values you need is the way forward, and the try: except: block inside the session_status function isn't doing anything at the moment.
I have a piece of code that acts as a listener of a button and evaluates whether some fields above this button are filled in:
def goListener(self):
if all( [self.nme.get() != "", self.dsc.get() != "", self.imp.get != ""] ):
name = self.nme.get()
desc = self.dsc.get()
while True:
try:
imp = int(self.imp.get())
break
except:
imp = int(self.imp.get())
When I run this program with different fields filled in or otherwise, it gets it right and produces the error message I ask it to with every combination except where nme and dsc are filled in but imp isn't, this produces the error message:
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1442, in __call__
return self.func(*args)
File "C:\Python33\todo.py", line 68, in goListener
imp = int(self.imp.get())
ValueError: invalid literal for int() with base 10: ''
This is running the except block by the way, which it shouldn't. Is this a problem with the evaluation, or am I missing something here?
You have:
self.imp.get != ""
You are failing to invoke the .get() method. Try:
self.imp.get() != ""
If imp = int(self.imp.get()) throws an error, calling it again outside of a try block will throw the same error.
The except block is for code that should run when there is an exception:
try:
imp = int(self.imp.get())
break
except:
print "Something bad happened"