milesrichardson / ParsePy

A relatively up-to-date fork of ParsePy, the Python wrapper for the Parse.com API. Originally maintained by @dgrtwo
MIT License
516 stars 184 forks source link

"invalid JSON" when saving object #113

Closed flound1129 closed 8 years ago

flound1129 commented 9 years ago

very simple code throwing this error. I think something is not being escaped properly?

class AutoQuestion(Object):
    pass

questions = AutoQuestion.Query.filter(email_sent=False).limit(10).skip(skip)

    for question in questions:
        question.email_sent = True
        question.save()

Traceback (most recent call last): File "/usr/local/bin/email_followers.py", line 37, in question.save() File "/usr/local/lib/python2.7/dist-packages/parse_rest-0.2.20141004-py2.7.egg/parse_rest/datatypes.py", line 350, in save return self._update(batch=batch) File "/usr/local/lib/python2.7/dist-packages/parse_rest-0.2.20141004-py2.7.egg/parse_rest/datatypes.py", line 368, in _update response = self.class.PUT(self._absolute_url, batch=batch, _self._to_native()) File "/usr/local/lib/python2.7/dist-packages/parse_rest-0.2.20141004-py2.7.egg/parse_rest/connection.py", line 138, in PUT return cls.execute(uri, 'PUT', _kw) File "/usr/local/lib/python2.7/dist-packages/parse_rest-0.2.20141004-py2.7.egg/parse_rest/connection.py", line 124, in execute raise exc(e.read()) parse_rest.core.ResourceRequestBadRequest: {"code":107,"error":"invalid JSON"}

flound1129 commented 9 years ago

Here's the object in question:

 {'body': u"<div> <p><strong>Of all the things that drones are changing, it might be the way we view sports that could have the biggest impact. </strong></p>\n<p><figure><img width=100% src=https://d262ilb51hltx0.cloudfront.net/max/800/1*gFSjfUIcITg39YEbsUbiTw.jpeg></figure></p><p>Drones will allow us to view sports in totally new ways, creating fresh excitement for sporting events. </p> <h3 class=graf--h3>Here's five badass videos filmed from a drone of people doing awesome sports: </h3> <h2 class=graf--h3>1. Electric Skateboarding</h2> <p>Skateboards powered by electric motors and Tesla-inspired battery technology are here. Now, people can easily skate on flat pavement and even uphill. This video of skaters flying above San Francisco's skyline show how awesome this can be. </p> \n<div class=embed-responsive-16by9><iframe class=embed-responsive-item width=560 height=315 src=https://www.youtube.com/embed/3aO00b2QwHc?rel=0&amp;showinfo=0 frameborder=0 allowfullscreen></iframe></div>\n<h2 class=graf--h3>2. Wakeboarding</h2> <p>Aerial shots of wakeboarders have always been difficult because of how fast they move. However, drones can keep pace with the boats pulling wakeboarders. Drones can even keep up with a wakeboarder gliding down a mountain of rice paddies.</p>\n<div class=embed-responsive-16by9><iframe class=embed-responsive-item width=560 height=315 src=https://www.youtube.com/embed/E17RdQe5QoI?rel=0&amp;showinfo=0 frameborder=0 allowfullscreen></iframe></div>\n<h2 class=graf--h3>3. Ninja Duels</h2> <p>Before drones, your average ninja duel was tough to follow. Now, there is technology to stay up close to these epic warrior battles. It wouldn't be surprising to see ninja duels become a fixture on sports channels the way lawnmower racing and no-limit poker games have.</p>\n<iframe src=https://player.vimeo.com/video/36341233?color=d6d6d6&title=0&byline=0&portrait=0 width=500 height=209 frameborder=0 webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>\n<h2 class=graf--h3>4. Surfing</h2> <p>One of the earliest sports to adopt drone technology, this footage of surfers in crystal-clear waters is really compelling. Previously, these types of shots were expensive and/or almost impossible because only helicopters were capable of doing them.</p>\n<div class=embed-responsive-16by9><iframe class=embed-responsive-item width=560 height=315 src=https://www.youtube.com/embed/Xm49yEL6ca4?rel=0&amp;showinfo=0 frameborder=0 allowfullscreen></iframe></div>\n<h2 class=graf--h3>5. Drone Racing</h2> <p>If you're a fan of the <em>Return of the Jedi </em>racing scene, you're probably going to like this drone racing video. Not only can drones shoot crazy video of people doing cool athletic feats, they can also film other drones fighting it out to see which one is fastest. </p>\n<div class=embed-responsive-16by9><iframe class=embed-responsive-item width=560 height=315 src=https://www.youtube.com/embed/6zDDsX5xYcA?rel=0&amp;showinfo=0 frameborder=0 allowfullscreen></iframe></div>\n<h3 class=graf--h3>What's the best drone video you've ever seen? Answer and get 1,000 bits!</h3></div>\n\n\n\n\t\t", 'category': [u'featured'], '_updated_at': <parse_rest.datatypes.Date object at 0x7f8f6a142c90>, 'objectId': u'hwWdwSUEaX', 'tip_total': 250, 'tagline': u"You don't need a helicopter anymore to get badass shots of awesome sports.", 'asker': u'MAARQpt3gD', 'question': u'[answer and get 1,000 bits] 5 Sports That Look Awesome Filmed By Drones', 'email_sent': False, '_created_at': <parse_rest.datatypes.Date object at 0x7f8f6a142b50>, 'answered': [u'eYoLYaGbxh', u'2K0NuiQTYl', u'YJeClpEwe0'], 'featuredAt': datetime.datetime(2015, 5, 21, 16, 7, 24, 670000), 'zap': [u'drones', u'bitcoin'], 'answercount': 3, 'likecount': 0, 'postimage': u'https://d262ilb51hltx0.cloudfront.net/max/2000/1*uqo67LxFqBVQ5kxeNMM5Gw.jpeg', 'tip_count': 1}
lullis commented 9 years ago

The _created_at, _updated_at attributes seem suspicious. They are supposed to be transformed into actual python date objects - pretty much like your "featureAt". How were these AutoQuestion objects were created originally - through the parse_rest library or directly through the dashboard, or some other app?

flound1129 commented 9 years ago

This one was created manually via the Parse data browser.

emichaud commented 9 years ago

Hi, I'm wondering if you resolved this issue? I have a similar issue that I've tracked down to occurring whenever I try to update a column that is named "body". No matter what I tried, whenever I tried to update the column, it would fail. If I removed the update to the column named 'body', the update would succeed. If I put the text into another column, that worked as well. I implemented a work around using an after save trigger to copy the data from a temp column to the 'body' column outside of the library. I'm just learning python and after some code review I could not see anything that would cause this issue. Hope that helps.

dankrause commented 9 years ago

Due to the way ParsePy passes around parse object properties, it looks like there are a number of column names that, when used, will cause a number of different kinds of problems. Other problematic names include: "batch", "extra_headers", "cls", and "http_verb". These are named arguments in a HTTP request method that are trampled by kwargs containing object properties. This is a design flaw in ParsePy, and I'm looking into a good way to correct it.

EDIT: This is a deep problem that spans much of the ParsePy codebase. It'll take a while to fix.

dankrause commented 9 years ago

@emichaud @flound1129 - try the commit above. It doesn't fix the design flaw, but it gets the troublesome named argument out of your way. You can now use "body" as a column name without trouble, but you'll still run into the same problem if you try to use "_body" as the column name. The design flaw will take some time to actually fix, so hopefully this workaround does the job for now.

emichaud commented 9 years ago

Dan, thanks for trying to address this concern. I pulled the new version today and tried it, however it still did not work. Here are the errors I received when I tried to save some text into a column named ‘body

here is simple test:

tc = testClass.Query.get(objectId='cm7QmBJY5B') print('lucky number:',tc.luckyNumber) #confirm I got the object tc.body = 'Here is the body data' tc.save()

which produces these errors:

Traceback (most recent call last): File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/connection.py", line 118, in execute response = urlopen(request, timeout=CONNECTION_TIMEOUT) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 161, in urlopen return opener.open(url, data, timeout) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 469, in open response = meth(req, response) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 579, in http_response 'http', request, response, code, msg, hdrs) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 507, in error return self._call_chain(_args) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 441, in _call_chain result = func(_args) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 587, in http_error_default raise HTTPError(req.full_url, code, msg, hdrs, fp) urllib.error.HTTPError: HTTP Error 400: Bad Request

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "bbrefGameFix.py", line 21, in tc.save() File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/datatypes.py", line 350, in save return self._update(batch=batch) File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/datatypes.py", line 368, in _update response = self.class.PUT(self._absolute_url, batch=batch, _self._to_native()) File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/connection.py", line 140, in PUT return cls.execute(uri, 'PUT', _kw) File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/connection.py", line 126, in execute raise exc(e.read()) parse_rest.core.ResourceRequestBadRequest: b'{"code":107,"error":"invalid JSON"}\n'

On Jun 22, 2015, at 9:26 AM, Dan Krause notifications@github.com wrote:

@emichaud https://github.com/emichaud @flound1129 https://github.com/flound1129 - try the commit above. It doesn't fix the design flaw, but it gets the troublesome named argument out of your way. You can now use "body" as a column name without trouble, but you'll still run into the same problem if you try to use "_body" as the column name. The design flaw will take some time to actually fix, so hopefully this workaround does the job for now.

— Reply to this email directly or view it on GitHub https://github.com/dgrtwo/ParsePy/issues/113#issuecomment-114101266.

dankrause commented 9 years ago

I'm not able to duplicate that. Here's an example ipython session:

In [2]: o1 = Object()

In [3]: o1.foo = "bar"

In [4]: o1.save()

In [5]: o1.objectId
Out[5]: u'Y5vILbdbCN'

In [6]: o2 = Object.Query.get(objectId=o1.objectId)

In [7]: o2.objectId
Out[7]: u'Y5vILbdbCN'

In [8]: o2.body = "test"

In [9]: o2.save()

In [10]: o3 = Object.Query.get(objectId=o2.objectId)

In [11]: o3.objectId
Out[11]: u'Y5vILbdbCN'

In [12]: o3.body
Out[12]: u'test'

It's possible you're running into an unrelated bug. Try out my example session to make sure that you're not running into the body-specific bug, and I'll dig into your error to see what might cause such a thing.

EDIT: Could you provide me with a minimal example that allows me to duplicate this? It seems to me that something in ParsePy is not properly handling some bit of data you're trying to write to an object, and I'm not able to find a bit of data that causes the same issue.

emichaud commented 9 years ago

Hi Dan,

Appreciate your help and feedback! I tried this from the python interpreter like you suggested. Please note, I’m new to python and just getting my legs under me, so I may not follow exactly what you would like me to test. I only imported the object and register code. I still got an error when I tried to save. Here is the transcript:

o1 = Object() o1.foo = "bar" o1.save() o1.objectId 'qgzj3ioaEL' o2 = Object.Query.get(objectId=o1.objectId) o2.objectId 'qgzj3ioaEL' o2.body = "test" o2.save() Traceback (most recent call last): File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/connection.py", line 118, in execute response = urlopen(request, timeout=CONNECTION_TIMEOUT) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 161, in urlopen return opener.open(url, data, timeout) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 469, in open response = meth(req, response) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 579, in http_response 'http', request, response, code, msg, hdrs) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 507, in error return self._call_chain(_args) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 441, in _call_chain result = func(_args) File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/request.py", line 587, in http_error_default raise HTTPError(req.full_url, code, msg, hdrs, fp) urllib.error.HTTPError: HTTP Error 400: Bad Request

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "", line 1, in File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/datatypes.py", line 350, in save return self._update(batch=batch) File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/datatypes.py", line 368, in _update response = self.class.PUT(self._absolute_url, batch=batch, _self._to_native()) File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/connection.py", line 140, in PUT return cls.execute(uri, 'PUT', _kw) File "/Users/ev/dev/pythondev/flasky/venv/lib/python3.4/site-packages/parse_rest/connection.py", line 126, in execute raise exc(e.read()) parse_rest.core.ResourceRequestBadRequest: b'{"code":107,"error":"invalid JSON"}\n'

On Jun 23, 2015, at 8:33 AM, Dan Krause notifications@github.com wrote:

I'm not able to duplicate that. Here's an example ipython session:

In [2]: o1 = Object()

In [3]: o1.foo = "bar"

In [4]: o1.save()

In [5]: o1.objectId Out[5]: u'Y5vILbdbCN'

In [6]: o2 = Object.Query.get(objectId=o1.objectId)

In [7]: o2.objectId Out[7]: u'Y5vILbdbCN'

In [8]: o2.body = "test"

In [9]: o2.save()

In [10]: o3 = Object.Query.get(objectId=o2.objectId)

In [11]: o3.objectId Out[11]: u'Y5vILbdbCN'

In [12]: o3.body Out[12]: u'test' It's possible you're running into an unrelated bug. Try out my example session to make sure that you're not running into the body-specific bug, and I'll dig into your error to see what might cause such a thing.

— Reply to this email directly or view it on GitHub https://github.com/dgrtwo/ParsePy/issues/113#issuecomment-114477701.

dankrause commented 9 years ago

@emichaud: I'm not able to duplicate that. It kind of looks like you haven't applied the changes I've made. To confirm that my changes are there, run this:

from parse_rest.connection import ParseBase
import inspect
print(inspect.signature(ParseBase.execute))

If you have my changes, you'll see this:

(uri, http_verb, extra_headers=None, batch=False, _body=None, **kw)

If not, you'll see this:

(uri, http_verb, extra_headers=None, batch=False, body=None, **kw)

The only difference there is the underscore before the word body.

emichaud commented 9 years ago

ok, you are correct. I did not get your changes originally. I’m sorry that I caused you to recycle that troubling shooting process due to my error. I had re-installed the package using the pip install command after your commit and it worked but it must have just installed the previous version. I just fixed it by downloading and copying in the library over into the site_packages directory.

How should I have updated my environment after your commit? Sorry if that seems so basic, as I mentioned I’m diving into Python for the first time and I’ve been struggling with trying to get a solid python3 environment going and I’m a bit confused with the different approaches and on updating libraries, etc. Any suggestions would be appreciated.

Just to confirm, I do have your latest code installed now and I when I re-ran my test case, it worked perfectly this time. Thanks so much for your help. Everett

On Jun 24, 2015, at 10:10 AM, Dan Krause notifications@github.com wrote:

@emichaud https://github.com/emichaud: I'm not able to duplicate that. It kind of looks like you haven't applied the changes I've made. To confirm that my changes are there, run this:

from parse_rest.connection import ParseBase import inspect print(inspect.signature(ParseBase.execute)) If you have my changes, you'll see this:

(uri, http_verb, extra_headers=None, batch=False, _body=None, **kw) If not, you'll see this:

(uri, http_verb, extra_headers=None, batch=False, body=None, **kw) The only difference there is the underscore before the word body.

— Reply to this email directly or view it on GitHub https://github.com/dgrtwo/ParsePy/issues/113#issuecomment-114879911.

dankrause commented 9 years ago

Take a look at the pip documentation. It depends on what you're trying to accomplish, but read about the --upgrade, --force-reinstall and --editable flags.