Preventing python from terminating if json data not found - python

While iterating through json there are some files which does not have the field i am parsing (using iterator as test_category_list = [info["Test Caps"][0]] ). Python is terminating the execution with error KeyError: 'Test Caps' . I wanted give a default test_category_list if no key found. How I will do that in python ?

dict.get(key[, default]) accepts a second argument that is returned if no key is found (you can return any python object; for the example i return the string 'no_key_found'):
dct = {'key': 'value'}
print(dct.get('key', 'no_key_found')) # -> 'value'
print(dct.get('no_key', 'no_key_found')) # -> 'no_key_found'
or you catch the KeyError in a try/except block:
try:
ret = dct['key']
except KeyError:
ret = 'no_key_found'
note: be sure to only catch the KeyError and not any other kind of exception that might occur there.
you could also check if the key exists before you access it (if 'key' in dct: ...); this is discouraged: the common python coding style prefers EAFP over LBYL.

One option is to use exceptions. They attempt to run the code in the 'try' section, and if that fails they run the except and continue through the script.
try:
test_category_list = [info["Test Caps"][0]]
except Keyerror:
test_category_list = 'error'

Related

Python ValueError Exception not being caught properly

itemis a python dictionary
print item.get('body')
gives the following output in some cases:
"1211V1"
however, item.get('body') mostly has a unicode string of the format:
u'{"points_token_id":"327727a0-3909-4132-8fa2-ee45146add1e"}'
I needed to convert the above unicode string to a python dictionary. So I am doing this:
try:
body_dic = json.loads(item.get('body'))
body_string = ""
for body_item in body_dic.keys():
body_string += body_item + ": {'required': True, 'type': 'resource', 'value': " + str(body_dic.get('body_item')) + "\n\t\t\t\t"
except Exception as e:
print "futt gayaa"
print type(e).__name__
print e.args
body_string = item.get('body')
and then a bunch of code after this. So in the above the moment item.get('body') comes out to be "1211V1" a ValueError Exception should be raised and the execution flow should get into the except block. Am I right ?
It does not get raised however and the execution flow continues to go onto the next line which is :
for body_item in body_dic.keys():
and then the following exception gets raised:
AttributeError
("'unicode' object has no attribute 'keys'",)
which I get to know if I change the except block in the above to catch a generic exception as :
except Exception as e:
print "futt gayaa"
print type(e).__name__
print e.args
body_string = item.get('body')
Please help me understand this. In my opinion the moment the first exception gets raised (which in our case should be the ValueError Exception) the control flow should go into the catch block. Why does it go to the next line of code and then when the Attribute Exception gets raised does it get caught.
Assuming that, as you wrote
print item.get("body")
returns literally
"1211V1"
then the quotation marks are part of the string itself.
So you effectively calling
json.loads('"1211V1"')
where you are loading a JSON string literal--perfectly valid. Then, of course, you get an AttributeError for trying to call .keys() on a unicode object.
If you're using print to debug a problem it might mislead you in this way--you're better off often, if you really want to be sure what the object is that you're having trouble with, writing print(repr(obj)). In this case that would tell you that item.get("body") is u'"1211V1"'.
So your problem is that you have a sequence of dicts, whose attribute body is a JSON string. It may either be:
"1211V1"
or:
{
"points_token_id":"327727a0-3909-4132-8fa2-ee45146add1e"
}
This is, a JSON string or a JSON object. By json.loads()ing this string you are always getting a valid Python value, either a Python str or a Python dict, respectively. What you want to do is detect if its one or another:
json_body = json.loads(item['body'])
if type(json_body) is dict:
for key, value in json_body.items():
json_body[key] = {'required': True, 'type': 'resource', 'value': value}
body_string = json.dumps(json_body)
else:
pass # Handle the "1211V1"

Which key failed in Python KeyError?

If I catch a KeyError, how can I tell what lookup failed?
def poijson2xml(location_node, POI_JSON):
try:
man_json = POI_JSON["FastestMan"]
woman_json = POI_JSON["FastestWoman"]
except KeyError:
# How can I tell what key ("FastestMan" or "FastestWoman") caused the error?
LogErrorMessage ("POIJSON2XML", "Can't find mandatory key in JSON")
Take the current exception (I used it as e in this case); then for a KeyError the first argument is the key that raised the exception. Therefore we can do:
except KeyError as e: # One would do it as 'KeyError, e:' in Python 2.
cause = e.args[0]
With that, you have the offending key stored in cause.
Expanding your sample code, your log might look like this:
def poijson2xml(location_node, POI_JSON):
try:
man_json = POI_JSON["FastestMan"]
woman_json = POI_JSON["FastestWoman"]
except KeyError as e:
LogErrorMessage ("POIJSON2XML", "Can't find mandatory key '"
e.args[0]
"' in JSON")
It should be noted that e.message works in Python 2 but not Python 3, so it shouldn't be used.
Not sure if you're using any modules to assist you - if the JSON is coming in as a dict, one can use dict.get() towards a useful end.
def POIJSON2DOM (location_node, POI_JSON):
man_JSON = POI_JSON.get("FastestMan", 'No Data for fastest man')
woman_JSON = POI_JSON.get("FastestWoman", 'No Data for fastest woman')
#work with the answers as you see fit
dict.get() takes two arguments - the first being the key you want, the second being the value to return if that key does not exist.
If you import the sys module you can get exception info with sys.exc_info()
like this:
def POIJSON2DOM (location_node, POI_JSON):
try:
man_JSON = POI_JSON["FastestMan"]
woman_JSON = POI_JSON["FastestWoman"]
except KeyError:
# you can inspect these variables for error information
err_type, err_value, err_traceback = sys.exc_info()
REDI.LogErrorMessage ("POIJSON2DOM", "Can't find mandatory key in JSON")

Python error exception handling with less return statements?

I have a function with a try catch block where I have:
def testfunction():
try:
good = myfunction()
return good
except ExceptionOne:
error="Error ExceptionOne".
return error
except ExceptionTwo:
error="Error ExceptionTwo".
return error
except ExceptionThree:
error="Error ExceptionThree".
return error
Is there a way such that I could structure this in such a way where have a single return statement for all the exceptions?
What about something like:
def testfunction():
try:
good = myfunction() # or `return myfunction()` if the case is actually this simple
except (ExceptionOne, ExceptionTwo, ExceptionThree) as err:
error = 'Error %s' % err.__class__.__name__
return error
return good
Of course, I'm not exactly sure what the purpose of this is. Why not just let the exception propogate and handle it higher up? As it is, you need logic to check the return value to see if it's good or not anyway, I think it would be cleaner if that logic was bound up in exception handling rather than in type checking or string value checking ...

Try two expressions with `try except`

I have two expressions. I need to try one expression, if it is raise an exception try another, but if the second raises an exception too - to raise the exception.
I tried this, but it is looks ugly and I am not sure it is the best way to solve this issue:
try:
image = self.images.order_by(func.random()).limit(1)
except:
try:
image = self.images.order_by(func.rand()).limit(1)
except ProgrammingError:
raise ProgrammingError(
'The database engine must be PostgtreSQL or MySQL')
How do you do it?
Making a separate function is very helpful.
def get_random_image(self):
for rand in func.random, func.rand:
try:
return self.images.order_by(rand()).limit(1)
except ProgrammingError:
pass
raise ProgrammingError('This database engine is not supported')
Use a loop:
for methname in ("random", "rand"):
try:
image = self.images.order_by(getattr(func, methname)()).limit(1)
break
except ProgrammingError:
continue
else:
raise ProgrammingError("The database engine must be PostgtreSQL or MySQL")
The loop's else clause is executed only if the loop terminates normally (i.e., without a break) which is why we break after doing the image assignment. If you consider this too tricksy, because the else clause is so infrequently used with for, then this would also work:
image = None
for methname in ("random", "rand"):
try:
image = self.images.order_by(getattr(func, methname)()).limit(1)
except ProgrammingError:
continue
if not image:
raise ProgrammingError("The database engine must be PostgtreSQL or MySQL")
In this particular case, I'd actually try and detect the database before selecting the function. Can you reach the database connection from your code? If so, just switch on the drivername:
random = None
if drivername == 'postgres':
random = func.random
elif drivername == 'mysql':
random = func.rand
else:
raise ValueError('This module requires that you use PostgreSQL or MySQL')
Then, when selecting images, use the random value:
image = self.images.order_by(random()).limit(1)
Actually it MIGHT be a design flaw. Raising exceptions is to act on an event that should normally not occur. If you want to do something functionally into an except (except for handling the exception), than it looks like the first statement that you want to try is not a statement that should get an exception at all.
So instead of:
try:
do statement 1
except ...
try:
do statement 2
except:
think about :
if (statement_1 result == ...)
try:
do statement 2
except:
If you want to check if rand or random is a function of a class, you also could use
if 'rand' in dir(some object of a class)

Django/Python - Try/except problem

i have code like this:
try:
var = request.POST['var']
except NameError:
var = ''
Why always code after "except" is executing? Even if request.POST['var'] exist.
How do you know it is executing? Perhaps request.POST['var'] is also '' so you couldn't tell the difference.
Also, the only way that
var = request.POST['var']
could raise a NameError is if request doesn't exist.
If request.POST doesn't exist, means POST doesn't exist as an attribute of request thus raising AttributeError instead, and if request.POST['var'] doesn't exist, means 'var' is not a key of request.POST thus raising KeyError instead.
EDIT:
My guess is that you're not sending a POST. But can't know for sure.
Eliminate the guesswork and replace NameError with something like KeyboardInterrupt, look at the traceback and you'll know exactly what the problem is.
A better way to do what you seem to be trying to do might be
var = request.POST.get('var', 'some default value')
where the second argument to the POST dict's get method is the value to return if the key ('var' in this case) doesn't exist. Translating your example exactly would result in:
var = request.POST.get('var', '')
That way, no try...except block or conditional statements are needed.
what's the result of the following in your case?
except NameError, e:
print e
try
try:
if request.method == 'POST':
var = request.POST['var']
except NameError:
var = ''

Categories

Resources