I would like to get the key name from the Python KeyError exception:
For example:
myDict = {'key1':'value1'}
try:
x1 = myDict['key1']
x2 = myDict['key2']
except KeyError as e:
# here i want to use the name of the key that was missing which is 'key2' in this example
print error_msg[missing_key]
i have already tried this
print e
print e.args
print e.message
my code is inside django view !
if i use ipython for example and try e.arg or e.message it works fine.
but then i try it while inside a django view i get this results:
"Key 'key2' not found in <QueryDict: {u'key1': [u'value']}>"
("Key 'key2' not found in <QueryDict: {u'key1': [u'value']}>",)
Key 'key2' not found in <QueryDict: {u'key1': [u'value']}>
while i just want the 'key2'
You can use e.args:
[53]: try:
x2 = myDict['key2']
except KeyError as e:
print e.args[0]
....:
key2
From the docs:
The except clause may specify a variable after the exception name (or
tuple). The variable is bound to an exception instance with the
arguments stored in instance.args
myDict = {'key1':'value1'}
try:
x1 = myDict['key1']
x2 = myDict['key2']
except KeyError as e:
# here i want to use the name of the key that was missing which is 'key2' in this example
print "missing key in mydict:", e.message
For KeyErrors, the 2nd element (index 1) of sys.exc_info() holds the key:
import sys
try:
x1 = myDict['key1']
x2 = myDict['key2']
except KeyError:
print 'KeyError, offending key:', sys.exc_info()[1]
Output:
KeyError, offending key: 'key2'
Simply, the response of the KeyError is the key itself in case you want to make use of it you just need to call the error as it is.
myDict = {'key1':'value1'}
try:
x1 = myDict['key1']
x2 = myDict['key2']
except KeyError as missing_key:
# Used as missing_key for readability purposes only
print(f"Trying to access a <dict> with a missing key {missing_key}")
P.S. This was tested on Python version 3.8.12
As your dictionary is actually a QueryDict, and therefore returns a different error message, you will need to parse the exception message to return the first word in single quotes:
import re
#...
except KeyError as e:
m = re.search("'([^']*)'", e.message)
key = m.group(1)
Related
Is there a way to print out the contents of a dictionary after raising an exception?
Assuming that the dictionary is this,
d = {"orange":[1,2], "yellow":[5], "red":[2,6,7]}
I would like it to look something like this:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-63-719ba08e2a6c> in <module>
----> 1 raise TypeError("This is a TypeError. Printing out contents of dictionary...")
TypeError: This is a TypeError.
orange
1
2
yellow
5
red
2
6
7
Is this possible?
Append the dictionary to the exception message.
raise TypeError("This is a TypeError. Printing out contents of dictionary...\n" + str(d))
The format is not right, but this does the trick.
d = {"orange":[1,2], "yellow":[5], "red":[2,6,7]}
try:
d["melon"]
except:
raise Exception(f"{str(d)}")
or you can do:
try:
d["melon"]
except:
for k in d.keys():
print(k, d[k])
try:
# code here
except Exception as e:
# Try and replace `Exception` with the error you're getting
print(f"Error = {e}")
for name, val in zip(d.keys(), d.values()):
print(name)
for i in val:
print(i)
Just be mindful that with except Exception:, the try loop will except any error
I have a block of code where exception is happening for some records while executing an imported method (pls see below). My goal is NOT to stop execution, but perhaps print (to understand what's wrong with the data) some values when in error and continue. I tried various way to use "try...except", but no luck! Can someone pls take a look and suggest? Many thanks in advance!
code below
if student_name not in original_df_names_flat:
for org in orgs:
data = calculate(org, data)
result = imported_module.execute(data) # here's the line where exception happens
return result
else:
return data
if student_name not in original_df_names_flat:
for org in orgs:
data = calculate(org, data)
result = None # May fail to get assigned if error in imported_module
try:
result = imported_module.execute(data) # here's the line where exception happens
except Exception as e:
print(f'{e}') # probably better to log the exception
return result
else:
return data
I guess an exception can occur in 2 places. So try this. It covers your exception line and the other possibility. So your program should keep running.
Secondly I have adjusted the indentation of the second try clause so the loop processes all data in orgs. To return all results I have added a list 'results'.
if student_name not in original_df_names_flat:
results = []
for org in orgs:
try:
data = calculate(org, data)
except Exception as e:
print(str(e))
try:
result = imported_module.execute(data) # here's the line where exception happens
results.append(result)
except Exception as e:
print(str(e))
return results
else:
return data
the below solution took care of the problem:
if student_name not in original_df_names_flat:
for org in orgs:
data = calculate(org, data)
try:
result = imported_module.execute(data) # here's the line where exception happens
except exception as e:
print ("exception happened", e)
pass
return data
return result
else:
return data
the above solution took care of the problem
I'm running the following:
for server in server_list:
for item in required_fields:
print item, eval(item)
There is a possibility that some keys may not exist, but worse it's represented on a parent key not the one I'm scanning for.
So I'm scanning the json for the following key:
server['server_management']['server_total_cost_of_ownership']['description']
Which doesn't exist but it's actually the parent that is null:
server['server_management']['server_total_cost_of_ownership']
How do I write my code to account for this? It's not giving a key error. Right now I get the following traceback:
Traceback (most recent call last):
File "C:/projects/blah/scripts/test.py", line 29, in <module>
print item, eval(item)
File "<string>", line 1, in <module>
TypeError: 'NoneType' object has no attribute '__getitem__'
Full code:
import csv
import json
import os
import requests
import sys
required_fields = ["server['server_name']","server['server_info']['asset_type']['display_name']",
"server['asset_status']['display_name']", "server['record_owner']['group_name']",
"server['server_management']['server_total_cost_of_ownership']['description']",
"server['server_management']['primary_business_owner']['name']",
"server['environment']['display_name']", "server['is_virtual']",
"server['managed_by']['display_name']", "server['server_info']['billable_ibm']",
"server['server_info']['billing_sub_type']['display_name']",
"server['server_info']['serial_number']", "server['location']['display_name']",
"server['inception_date']", "server['server_info']['decommission_date']" ]
# Query API for all servers
def get_servers_info():
servers_info = requests.get('url')
return servers_info.json()
def get_server_info(sid):
server_info = requests.get('url')
return server_info.json()
server_list = get_servers_info()
for server in server_list:
for item in required_fields:
print item, eval(item)
In fact you should avoid eval. After the json load since you know the key name, you can use a list to go deeper in the tree.
server['server_management']['primary_business_owner']['name']" => ["server_management', 'primary_business_owner', 'name']
Here a snippet for a json validation against a list of required fields.
data={
"d": {
"p":{
"r":[
"test"
]
}
},
"a": 3
}
def _get_attr(dict_, attrs):
try:
src = attrs[:]
root = attrs.pop(0)
node = dict_[root]
null = object()
for i, attr in enumerate(attrs[:]):
try:
node = node.get(attr, null)
except AttributeError:
node = null
if node is null:
# i+2 pop and last element
raise ValueError("%s not present (level %s)" % (attr, '->'.join(src[: i+2])))
return node
except KeyError:
raise ValueError("%s not present" % root)
# assume list of required field
reqs = [
["d", "p", "r"],
["d"],
["k"],
["d", "p", "r", "e"],
]
for req in reqs:
try:
_get_attr(data, req)
except ValueError as E:
print(E)
# prints
# k not present
# e not present (level d->p->r->e)
Ignoring the context of the code and not understanding the use of eval here, the way to do this is to use .get() and seed it with reasonable defaults.
For example:
server['server_management']['server_total_cost_of_ownership']['description']
Can be:
server.get('server_management', {}).get('server_total_cost_of_ownership', {}).get('description', '')
Then if any of the keys do not exist you will always get back an empty description ''.
Your problem here is totally unrelated to using eval[1]. The exception you get is the same as if the code would have been there directly. What you are running (via eval) is:
a = server['server_management']
b = a['server_total_cost_of_ownership']
c = b['description']
Yet, b is None, so resolving it to c will fail. Like a KeyError, you can also catch a TypeError:
for server in server_list:
for item in required_fields:
try:
print item, eval(item)
except TypeError:
print("Guess you're lucky you didn't include a fork bomb in your own code to eval.")
You may of course alternatively pass, print the offending item, open a browser to some page or do whatever error handling is appropriate given your input data.
[1] While not bickering around, I've made a new answer that works without eval. You can use precisely the same error handling:
for server in server_list:
for item in required_fields:
value = server
for key in parse_fields(field):
try:
value = value[key]
except TypeError:
print("Remember Kiddo: Eval is Evil!")
break
else: # for: else: triggers only if no break was issued
print item, value
I'm trying to extract data from an API, and I expect to receive KeyError and IndexError, so I use the try/except function to catch them. I first create a list of items that I'll loop through to extract information from the API responses. Then I create a dataframe that stores information about items that had no errors.
l= ["a","b","c","d"]
def extract_info_from_one_response(response, idx):
try:
response = response.json()
d = {
### some codes ##
}
## ERROR MANAGEMENT
except KeyError,e:
print idx, l[idx], str(e)
return {}
except IndexError,e:
print idx, l[idx], str(e)
return {}
dat = pd.DataFrame([extract_info_from_one_response(response, idx) for idx, response in enumerate(responses)], index=l)
When errors occur, Python prints out [1]the index of the problematic item, [2] the name of the item and [3]the details on the error that took place. How do I save/capture these three outputs, save them into objects or create a dataframe with these 3 pieces of information?
Are you asking how to trace the error?
If so,the traceback module would help like this:
In [1]: import traceback
In [2]: try:
...: a = []
...: a[2] = 1
...: except Exception:
...: y = traceback.format_exc()
...:
In [4]: print y
Traceback (most recent call last):
File "<ipython-input-2-ac34fe2721d3>", line 3, in <module>
a[2] = 1
IndexError: list assignment index out of range
I am not sure what you are asking, but if you want to save what is printed:
key_err = []
idx_err = []
def extract_info_from_one_response(response, idx):
try:
# your code here
except KeyError, e:
key_err.append((idx, l[idx], '{}'.format(e),))
return {}
except IndexError, e:
idx_err.append((idx, l[idx], '{}'.format(e),))
return {}
# your normal code here
# you need to return something here
dat = pd.DataFrame([extract_info_from_one_response(response, idx) for idx, response in enumerate(responses)], index=l)
print(key_err)
print(idx_err)
I wanted to create an IDN-aware formencode validator to use in one of my projects. I used a portion of code from the Django project (http://code.djangoproject.com/svn/django/trunk/django/core/validators.py) to do that, but there must be a trivial error in my code I can't find :
class Email(formencode.validators.Email):
def _to_python(self, value, state):
try:
return super(Email, self)._to_python(value, state)
except formencode.Invalid as e:
# Trivial case failed. Try for possible IDN domain-part
print 'heywo !'
if value and u'#' in value:
parts = value.split(u'#')
try:
parts[-1] = parts[-1].encode('idna')
except UnicodeError:
raise e
try:
super(Email, self)._to_python(u'#'.join(parts), state)
except formencode.Invalid as ex:
raise ex
return value
else:
raise e
When I try to validate an email with an IDN domain (ex: test#wääl.de), the Invalid exception raised by the first call is thrown, and the portion of code after the first except is never executed ('heywo !' is never printed).
There is an example :
>>> from test.lib.validators import Email
>>> Email().to_python(u'test#zääz.de')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python2.6/dist-packages/FormEncode-1.2.3dev-py2.6.egg/formencode /api.py", line 416, in to_python
vp(value, state)
File "/usr/local/lib/python2.6/dist-packages/FormEncode-1.2.3dev-py2.6.egg/formencode /validators.py", line 1352, in validate_python
value, state)
Invalid: The domain portion of the email address is invalid (the portion after the #: z\xe4\xe4z.de)
What did I do wrong ?
Thanks.
Okay, found the answer. I was overloading _to_python instead of validate_python. The class now looks like :
class Email(formencode.validators.Email):
def validate_python(self, value, state):
try:
super(Email, self).validate_python(value, state)
except formencode.Invalid as e:
# Trivial case failed. Try for possible IDN domain-part
if value and u'#' in value:
parts = value.split(u'#')
try:
parts[-1] = parts[-1].encode('idna')
except UnicodeError:
raise e
try:
super(Email, self).validate_python(u'#'.join(parts), state)
except formencode.Invalid as ex:
raise ex
else:
raise e
It's working perfectly :)