intercom / python-intercom

Python wrapper for the Intercom API.
https://keyes.ie/things/python-intercom/
Other
234 stars 145 forks source link

UnboundLocalError: local variable 'body' referenced before assignment #72

Closed greenafrican closed 9 years ago

greenafrican commented 9 years ago

Getting the below error when trying:

from intercom import User as User
from intercom import Intercom

Intercom.app_id = 'xxx'
Intercom.app_api_key = 'xxx'

user = User.create(email='me@example.com')

Error:

~/.../app/lib/python2.7/site-packages/intercom/request.pyc in parse_body(cls, resp)
     46         except ValueError:
     47             cls.raise_errors_on_failure(resp)
---> 48         if body.get('type') == 'error.list':
     49             cls.raise_application_errors_on_failure(body, resp.status_code)
     50         return body

UnboundLocalError: local variable 'body' referenced before assignment
jkeyes commented 9 years ago

I need to add some logging to help track problems down. It looks like there was an error but the status code is not currently handled by raise_errors_on_failure. Can you grab the status_code?

except ValueError:
    print("Error status code: %s" % (resp.status_code))
    cls.raise_errors_on_failure(resp)

I'll modify raise_errors_on_failure to throw an UnexpectedError if it's not a specific status_code.

Thanks.

greenafrican commented 9 years ago

Error status code: 200

jkeyes commented 9 years ago

So it's not that then :smile: Last thing, can you run this cURL command and include the output please:

curl https://api.intercom.io/users \
-X POST \
-u APP_ID:SECRET_ID \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' -d '
{
  "email": "me@example.com"
}'
greenafrican commented 9 years ago

Here you go:

{
    "type": "user",
    "id": "xxx",
    "user_id": null,
    "email": "me@example.com",
    "name": null,
    "avatar": {
        "type": "avatar",
        "image_url": null
    },
    "app_id": "xxx",
    "companies": {
        "type": "company.list",
        "companies": []
    },
    "location_data": {},
    "last_request_at": null,
    "last_seen_ip": null,
    "created_at": 1427820904,
    "remote_created_at": null,
    "signed_up_at": null,
    "updated_at": 1427820904,
    "session_count": 0,
    "social_profiles": {
        "type": "social_profile.list",
        "social_profiles": []
    },
    "unsubscribed_from_emails": false,
    "user_agent_data": null,
    "tags": {
        "type": "tag.list",
        "tags": []
    },
    "segments": {
        "type": "segment.list",
        "segments": []
    },
    "custom_attributes": {}
}
grantmcconnaughey commented 9 years ago

I'm receiving this same error while trying to create an event. My code:

def intercom_report_pdf_export(user, report_name, *args, **kwargs):
    event = Event.create(
        event_name="report-pdf-export",
        created_at=int(time.time()),
        user_id=user.pk,
        metadata={
            "report_name": report_name
        }
    )

    event.save()

The error:

Exception Type: UnboundLocalError
Exception Value:    
local variable 'body' referenced before assignment
Exception Location: /Users/grant/.virtualenvs/sidekick/lib/python2.7/site-packages/intercom/request.py in parse_body, line 48
jkeyes commented 9 years ago

I'll fix the ordering in the parse_body method. I don't know how you're encountering this and I haven't yet. Strange. What's version of Python are you using and on what OS?

Thanks for the reports.

grantmcconnaughey commented 9 years ago

Python 2.7.9, Mac OS X 10.10.2.

greenafrican commented 9 years ago

If it helps, I am also getting this warning:

InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail.

I'm on Python 2.7.6 & Mac OSX 10.10.2

jkeyes commented 9 years ago

That's because Python < 2.7.9 has pretty much non existent SSL support (I think).

grantmcconnaughey commented 9 years ago

Sure thing. I'll give it a shot tomorrow and report back then.

jkeyes commented 9 years ago

Haha trust me to leave my test config in there. Here's the monkey patch again:

import json
@classmethod
def my_parse_body(cls, resp):
    print "Using my_parse_body"
    try:
        body = json.loads(resp.content.decode())
        if body.get('type') == 'error.list':
            cls.raise_application_errors_on_failure(body, resp.status_code)
        return body
    except ValueError:
        cls.raise_errors_on_failure(resp)

from intercom.request import Request
Request.parse_body = my_parse_body

from intercom import User
from intercom import Intercom
Intercom.app_id = 'xxx'
Intercom.app_api_key = 'xxx'

user = User.create(email='me@example.com')
jkeyes commented 9 years ago

Thanks @grantmcconnaughey.

grantmcconnaughey commented 9 years ago

No such luck, @jkeyes. :frowning:

I ran this as my app was starting up:

@classmethod
def my_parse_body(cls, resp):
    print "Using my_parse_body"
    try:
        body = json.loads(resp.content.decode())
        if body.get('type') == 'error.list':
            cls.raise_application_errors_on_failure(body, resp.status_code)
        return body
    except ValueError:
        cls.raise_errors_on_failure(resp)

from intercom.request import Request
Request.parse_body = my_parse_body

"Using my_parse_body" output to the console. So far so good. Then this:

Traceback:
File "/Users/grant/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  112.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/grant/.virtualenvs/my_app/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/Users/grant/Dev/django/my_app/my_app/apps/company/decorators.py" in wrapper
  14.             return func(request, *args, **kwargs)
File "/Users/grant/Dev/django/my_app/my_app/apps/vehicle/views/company.py" in create
  153.                 vehicle = form.save()
File "/Users/grant/Dev/django/my_app/my_app/apps/vehicle/forms.py" in save
  176.             vehicle.save()
File "/Users/grant/.virtualenvs/my_app/lib/python2.7/site-packages/django/db/models/base.py" in save
  545.                        force_update=force_update, update_fields=update_fields)
File "/Users/grant/.virtualenvs/my_app/lib/python2.7/site-packages/django/db/models/base.py" in save_base
  582.                                    update_fields=update_fields, raw=raw, using=using)
File "/Users/grant/.virtualenvs/my_app/lib/python2.7/site-packages/django/dispatch/dispatcher.py" in send
  185.             response = receiver(signal=self, sender=sender, **named)
File "/Users/grant/Dev/django/my_app/my_app/apps/intercom/decorators.py" in wrapper
  8.             return func(*args, **kwargs)
File "/Users/grant/Dev/django/my_app/my_app/apps/intercom/events/vehicle.py" in intercom_vehicle_created
  16.                 "type": instance.get_type_display()
File "/Users/grant/.virtualenvs/my_app/lib/python2.7/site-packages/intercom/api_operations/save.py" in create
  13.         return cls(**response)

Exception Value: type object argument after ** must be a mapping, not NoneType

It seems to me that POSTing to the events API isn't returning a response. I gave it a try with curl and didn't receive any response. :( The events ARE saving to Intercom, though.

grantmcconnaughey commented 9 years ago

I monkey patched Save as well, and no exceptions were raised. Not sure if this is a good solution, though.

@classmethod
def create(cls, **params):
    from intercom import Intercom
    from intercom import utils
    collection = utils.resource_class_to_collection_name(cls)
    response = Intercom.post("/%s/" % (collection), **params)
    if response:
        return cls(**response)
    else:
        return cls()

from intercom.api_operations.save import Save
Save.create = create
jkeyes commented 9 years ago

@grantmcconnaughey Okay, I need to handle the case of an empty body. From the Intercom API docs:

Successful responses to submitted events return 202 Accepted with an empty body.

The monkey patch is only for you to try it out quickly see https://github.com/jkeyes/python-intercom/commit/714de5a97b03a204f9768c04a9fb8efd7bfcc7ca where I have it on a branch. I see the Ruby lib returns for an empty body:

return parsed_body if decoded_body.nil? || decoded_body.strip.empty?
grantmcconnaughey commented 9 years ago

Beautiful. I'll leave the patch until there is an alpha update. Thanks for all the awesome work you're doing on this. Let me know if there is any way I can help besides submitting the bugs I find.

Side note, can intercom/events.py be removed? I don't think that Event class is being used. I accidentally imported it yesterday and was very confused why it wasn't working. :wink:

jkeyes commented 9 years ago

Haha sneaky little events.py module in there. That should be removed alright.

As for helping, submitting issues is good and if that's what you're most comfortable at then flake ahead. You can submit PRs if you want for any issues if you want, but don't let it be a barrier for feedback.

jkeyes commented 9 years ago

Also from the Ruby lib in save.rb:

from_response(response) if response # may be nil we received back a 202

I've both places fixed now: https://github.com/jkeyes/python-intercom/commit/d98f10aafc380de33bcb713c801435558a012e09

jkeyes commented 9 years ago

Released 2.0.beta.