New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stringify response before json.loads #91

Merged
merged 6 commits into from Dec 10, 2016

Conversation

Projects
None yet
7 participants
@AroundTheLines
Contributor

AroundTheLines commented Sep 9, 2016

This fixes the "the json object must be str, not 'bytes'" error that happens on line 145.

Sample error before fix:

Traceback (most recent call last):
  File "(...)/twitter.py", line 53, in <module>
    async_data = LineItem.async_stats_job_data(account, async_stats_job_result['url'])
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/resource.py", line 261, in async_stats_job_data
    raw_body=True, stream=True).perform()
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/http.py", line 73, in perform
    response = self.__oauth_request()
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/http.py", line 103, in __oauth_request
    body=response.raw, raw_body=raw_response_body)
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/http.py", line 145, in __init__
    self._body = json.loads(raw_response_body)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/__init__.py", line 312, in loads
    s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'
Stringify response before json.loads
This fixes the "the json object must be str, not 'bytes'" error that happens on line 145.

Sample error before fix:
```
Traceback (most recent call last):
  File "(...)/twitter.py", line 53, in <module>
    async_data = LineItem.async_stats_job_data(account, async_stats_job_result['url'])
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/resource.py", line 261, in async_stats_job_data
    raw_body=True, stream=True).perform()
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/http.py", line 73, in perform
    response = self.__oauth_request()
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/http.py", line 103, in __oauth_request
    body=response.raw, raw_body=raw_response_body)
  File "(...)/venv/lib/python3.5/site-packages/twitter_ads/http.py", line 145, in __init__
    self._body = json.loads(raw_response_body)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/__init__.py", line 312, in loads
    s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'
```
@@ -141,6 +141,7 @@ def __init__(self, code, headers, **kwargs):
raw_response_body = self._raw_body

try:
raw_response_body = str(raw_response_body)

This comment has been minimized.

@frewsxcv

frewsxcv Sep 9, 2016

Contributor

This probably doesn't need to be in the try, unless str(..) raises a ValueError, which I don't think it does? Not that big of a deal though.

This comment has been minimized.

@AroundTheLines

AroundTheLines Sep 9, 2016

Contributor

That was how I found the code, so I didn't change that. I did just change location of the stringify just now though.

AroundTheLines added some commits Sep 9, 2016

Update for unicode convention
The reason this was erroring out is because some of the responses are strings and the others are bytes. By only decoding in the case of bytes we get valid python -> json objects from `LineItem.async_stats_job_data(...)` instead of huge line of bytes.
@@ -141,6 +141,8 @@ def __init__(self, code, headers, **kwargs):
raw_response_body = self._raw_body

try:
if not isinstance(raw_response_body, str):

This comment has been minimized.

@frewsxcv

frewsxcv Sep 12, 2016

Contributor

Under what scenario would raw_response_body not be a str?

This comment has been minimized.

@frewsxcv

frewsxcv Sep 12, 2016

Contributor

Just thinking out loud, if it's a str only when zlib.decompress gets called (a few lines above this), we should maybe move the decode() up in that conditional.

This comment has been minimized.

@AroundTheLines

AroundTheLines Sep 13, 2016

Contributor

I'm not entirely sure if that's the case, I'll test it out and get back to you on that.

@itsamaik

This comment has been minimized.

itsamaik commented Oct 18, 2016

Please merge this fix into the latest version, currently I'm also getting this error when retrieving job results. After zlib.decompress it's returning b'{ .......... }' thus resulting into an error when it's put into json.loads().

Edit: This seems to be an issue with Python 3+, see https://bugs.python.org/issue13989. TextIOWrapper could solve this.

@jbabichjapan

This comment has been minimized.

jbabichjapan commented Nov 24, 2016

@AroundTheLines Hey could you check the latest feedback and let us know if you are ready to go with this change? I don't have time to help drive this one in right now but if you need more eyeballs please call out and hopefully some people will notice and help review

@juanshishido

This comment has been minimized.

Contributor

juanshishido commented Dec 5, 2016

[Edited for clarity: changes only local]

As @frewsxcv suggested, I tested added adding .decode() to line 139:

raw_response_body = zlib.decompress(self._raw_body, 16 + zlib.MAX_WBITS).decode()

Async stats working locally for both Python 2.7.12 and Python 3.5.2.

Any thoughts on this, @AroundTheLines?

cc: @jbabichjapan, @tushdante

@juanshishido

This comment has been minimized.

Contributor

juanshishido commented Dec 8, 2016

@AroundTheLines: We'd like to merge this fix. Please let us know if you're able to make the aforementioned changes. If not, we can do it, too. Thanks for bringing this up and proposing a solution!

@AroundTheLines

This comment has been minimized.

Contributor

AroundTheLines commented Dec 9, 2016

@juanshishido: Hey, sorry I haven't been keeping up with this PR. I'd be glad to make the changes in a moment.

AroundTheLines added some commits Dec 9, 2016

@AroundTheLines

Like @juanshishido said, decompressing the response at the source solves python 3 compatibility issues, where bytes are returned instead of strings.

This should be ready for merging!
cc: @jbabichjapan, @tushdante

@juanshishido

LGTM :shipit:

@juanshishido juanshishido merged commit 13d44f0 into twitterdev:master Dec 10, 2016

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
@juanshishido

This comment has been minimized.

Contributor

juanshishido commented Dec 10, 2016

Awesome! Thanks, @AroundTheLines! 👍

@madman-bob

This comment has been minimized.

madman-bob commented Aug 29, 2017

I'm having this issue sporadically in version 1.2.2.

Eyeballing the source, I'm guessing we also need a .decode() in http.py:97. ie. Changing
raw_response_body = response.raw.read() if stream else response.text
to
raw_response_body = response.raw.read().decode() if stream else response.text.

Can anyone more knowledgable than I confirm/deny?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment