Python ValueError Exception not being caught properly - python

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"

Related

how to override an exception

I am writing a python / selenium script that trawls a website to find elements then ultimately write these element values to a table.
When it identifies that the element does not exist, it throws the NoSuchElementException as expected.
I have been trying to figure out how to override this exception, to instead, populate the table with a default value e.g. 'No Value'.
Problem is, python seems to ignore whatever I try, only to throw the exception regardless, resulting in the script execution ending prior to the table being able to be populated.
Any ideas?
file_1.py
def getAttributes(self):
...
...
...
try:
score = box.find_element(By.CLASS_NAME, 'dealClass').get_attribute('innerHTML').strip()
if not score:
raise NoSuchElementException(f"Score does not currently exist for {name}, as of {datetime.today()}")
except NoSuchElementException as e:
print('Exception is: ', e)
# Writing remediating code here but it never seems to work..
raise
...
file_2.py
def reportResults(self):
...
searchResults = self.find_element(By.ID, 'search_results_table')
report = ReportSearchResults)
table = PrettyTable(field_names=["Name", "Price", "Score"])
table.align["Name"] = "l"
table.align["Price"] = "l"
table.align["Score"] = "r"
table.add_rows(report.getAttributes())
print(table)
...
When you catch the exception, you can "fix" it by giving score a value. There's no need to reraise the exception at that point; you've handled it, so let your script proceed as if nothing happened.
def getAttributes(self):
...
try:
score = box.find_element(By.CLASS_NAME, 'dealClass').get_attribute('innerHTML').strip()
if not score:
# You're going to catch it, and you don't really need
# a long message telling you what went wrong. *You* raised
# this one, and you know why, and you're going to fix it.
raise NoSuchElementException("no score")
except NoSuchElementException as e:
score = 'No value'
...

Best practice for handling errors in python

try:
msg_json = json.loads(message_string)
if "task" in msg_json:
job_type = msg_json["task"]
return (job_type, msg_json)
logger.error(
"Could not parse message: must provide 'task' property",
extra={"message_string": message_string},
)
return empty
except Exception:
logger.exception(
"Error parsing JSON message. Did you accidentally double-escape it?",
extra={"message_string": message_string},
)
return empty
I have this code where i am trying to load some JSON formatted message string. After looking back at this piece of code i feel i maybe using try and catch in the wrong way and i was looking for suggestions as i am new to python and there may be cleaner approaches. There is no bug here but this is more for me to learn the "Cleaner" approach. As such i am open to all suggestions that explain the cleaner and more correct approach.
You could handle both of your error cases in catch blocks, which makes your "happy path" code a bit cleaner and neatly groups all the error handling in one place:
try:
msg_json = json.loads(message_string)
return (msg_json["task"], msg_json)
except KeyError:
logger.error(
"Could not parse message: must provide 'task' property",
extra={"message_string": message_string},
)
return empty
except Exception:
logger.exception(
"Error parsing JSON message. Did you accidentally double-escape it?",
extra={"message_string": message_string},
)
return empty

AttributeError: "str' object has no attribute 'text

I make script for shopify cart since it is impossible to purchase manually, when I ran the script to my command prompt,
it says ,
line 109, in AttributeError: "str' object has no attribute
'text
Scrape Product Info based on selected colors
if blue and cinder:
productInfo(urlBlueResponse.text)
productInfo(urlCinderResponse.text)
elif blue:
productInfo(urlBlueResponse.text)
elif cinder:
productInfo(urlCinderResponse.text)
else:
print(Fore.RED + timestamp
I was told it was from a capitalization mismatch, can somebody please explain this to me. I am new to coding and I want to learn all I can.
Based on the error message, either urlBlueResponse or urlCinderResponse (or both) are a string datatype. The way you are using them, it appears you expect these to be objects which have a text attribute. The error message is telling you that they're str objects and don't have text attributes.
This error happened when you tried to access an attribute on a string object -- .text -- that does not exist as an element on that object.
It looks like your code is working with HTTP request and response objects of some kind: urlBlueResponse
It is plausible that you got an error or some other unexpected behavior in the request/response cycle that resulted in one of the response objects returning a str (string) type instead of a response object with a text attribute. I suggest you handle the exception with a try/except block:
try:
if blue and cinder:
productInfo(urlBlueResponse.text)
productInfo(urlCinderResponse.text)
elif blue:
productInfo(urlBlueResponse.text)
elif cinder:
productInfo(urlCinderResponse.text)
else:
print(Fore.RED + timestamp)
except AttributeError as e:
#exception handler logic goes here
print("got exception: ")
print(e)
#if error indicates your request is recoverable then do so:
if recoverable(e):
do_request(again)
#if error is unrecoverable, decorate it and reraise it
#(See *Link)
# or just reraise it:
raise(e)
*Link: (Re-raise exception with a different type and message, preserving existing information)
urlBlueResponse = eval(urlBlueResponse)

Preventing python from terminating if json data not found

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'

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

Categories

Resources