Skip to content
This repository was archived by the owner on Jan 13, 2022. It is now read-only.

Commit dee95d8

Browse files
author
Jon Heaton
committed
Merge branch 'master' of github.com:seajosh/python-instagram into seajosh-master
2 parents 7194301 + 16412bd commit dee95d8

9 files changed

Lines changed: 65 additions & 45 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
python-instagram
44
======
5-
A Python client for the Instagram REST and Search APIs
5+
A Python 2/3 client for the Instagram REST and Search APIs
66

77
Installation
88
-----
@@ -12,6 +12,7 @@ Requires
1212
-----
1313
* httplib2
1414
* simplejson
15+
* six
1516

1617

1718
Instagram REST and Search APIs

instagram/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
from bind import InstagramAPIError, InstagramClientError
2-
from client import InstagramAPI
1+
from .bind import InstagramAPIError, InstagramClientError
2+
from .client import InstagramAPI

instagram/bind.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import urllib
2-
from oauth2 import OAuth2Request
2+
from .oauth2 import OAuth2Request
33
import re
4-
from json_import import simplejson
4+
from .json_import import simplejson
55
import hmac
66
from hashlib import sha256
7+
import six
8+
from six.moves.urllib.parse import quote
9+
import sys
710

811
re_path_template = re.compile('{\w+}')
912

1013

1114
def encode_string(value):
1215
return value.encode('utf-8') \
13-
if isinstance(value, unicode) else str(value)
16+
if isinstance(value, six.text_type) else str(value)
1417

1518

1619
class InstagramClientError(Exception):
@@ -76,7 +79,7 @@ def _build_parameters(self, args, kwargs):
7679
except IndexError:
7780
raise InstagramClientError("Too many arguments supplied")
7881

79-
for key, value in kwargs.iteritems():
82+
for key, value in six.iteritems(kwargs):
8083
if value is None:
8184
continue
8285
if key in self.parameters:
@@ -91,7 +94,7 @@ def _build_path(self):
9194
name = variable.strip('{}')
9295

9396
try:
94-
value = urllib.quote(self.parameters[name])
97+
value = quote(self.parameters[name])
9598
except KeyError:
9699
raise Exception('No parameter value found for path variable: %s' % name)
97100
del self.parameters[name]
@@ -130,7 +133,7 @@ def _do_api_request(self, url, method="GET", body=None, headers=None):
130133
raise InstagramClientError('Unable to parse response, not valid JSON.', status_code=response['status'])
131134

132135
# Handle OAuthRateLimitExceeded from Instagram's Nginx which uses different format to documented api responses
133-
if not content_obj.has_key('meta'):
136+
if 'meta' not in content_obj:
134137
if content_obj.get('code') == 420 or content_obj.get('code') == 429:
135138
error_message = content_obj.get('error_message') or "Your client is making too many request per second"
136139
raise InstagramAPIError(content_obj.get('code'), "Rate limited", error_message)
@@ -166,7 +169,7 @@ def _do_api_request(self, url, method="GET", body=None, headers=None):
166169
def _paginator_with_url(self, url, method="GET", body=None, headers=None):
167170
headers = headers or {}
168171
pages_read = 0
169-
while url and (pages_read < self.max_pages or self.max_pages is None):
172+
while url and (self.max_pages is None or pages_read < self.max_pages):
170173
api_responses, url = self._do_api_request(url, method, body, headers)
171174
pages_read += 1
172175
yield api_responses, url

instagram/client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import oauth2
2-
from bind import bind_method
3-
from models import MediaShortcode, Media, User, Location, Tag, Comment, Relationship
1+
from . import oauth2
2+
from .bind import bind_method
3+
from .models import MediaShortcode, Media, User, Location, Tag, Comment, Relationship
44

55
MEDIA_ACCEPT_PARAMETERS = ["count", "max_id"]
66
SEARCH_ACCEPT_PARAMETERS = ["q", "count"]

instagram/models.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from helper import timestamp_to_datetime
1+
from .helper import timestamp_to_datetime
2+
import six
23

34

45
class ApiModel(object):
@@ -12,7 +13,17 @@ def object_from_dictionary(cls, entry):
1213
return cls(**entry_str_dict)
1314

1415
def __repr__(self):
15-
return unicode(self).encode('utf8')
16+
return str(self)
17+
# if six.PY2:
18+
# return six.text_type(self).encode('utf8')
19+
# else:
20+
# return self.encode('utf8')
21+
22+
def __str__(self):
23+
if six.PY3:
24+
return self.__unicode__()
25+
else:
26+
return unicode(self).encode('utf-8')
1627

1728

1829
class Image(ApiModel):
@@ -36,7 +47,7 @@ class Media(ApiModel):
3647

3748
def __init__(self, id=None, **kwargs):
3849
self.id = id
39-
for key, value in kwargs.iteritems():
50+
for key, value in six.iteritems(kwargs):
4051
setattr(self, key, value)
4152

4253
def get_standard_resolution_url(self):
@@ -67,12 +78,12 @@ def object_from_dictionary(cls, entry):
6778
new_media.user = User.object_from_dictionary(entry['user'])
6879

6980
new_media.images = {}
70-
for version, version_info in entry['images'].iteritems():
81+
for version, version_info in six.iteritems(entry['images']):
7182
new_media.images[version] = Image.object_from_dictionary(version_info)
7283

7384
if new_media.type == 'video':
7485
new_media.videos = {}
75-
for version, version_info in entry['videos'].iteritems():
86+
for version, version_info in six.iteritems(entry['videos']):
7687
new_media.videos[version] = Video.object_from_dictionary(version_info)
7788

7889
if 'user_has_liked' in entry:
@@ -113,14 +124,14 @@ class MediaShortcode(Media):
113124

114125
def __init__(self, shortcode=None, **kwargs):
115126
self.shortcode = shortcode
116-
for key, value in kwargs.iteritems():
127+
for key, value in six.iteritems(kwargs):
117128
setattr(self, key, value)
118129

119130

120131
class Tag(ApiModel):
121132
def __init__(self, name, **kwargs):
122133
self.name = name
123-
for key, value in kwargs.iteritems():
134+
for key, value in six.iteritems(kwargs):
124135
setattr(self, key, value)
125136

126137
def __unicode__(self):
@@ -129,7 +140,7 @@ def __unicode__(self):
129140

130141
class Comment(ApiModel):
131142
def __init__(self, *args, **kwargs):
132-
for key, value in kwargs.iteritems():
143+
for key, value in six.iteritems(kwargs):
133144
setattr(self, key, value)
134145

135146
@classmethod
@@ -156,7 +167,7 @@ def __unicode__(self):
156167
class Location(ApiModel):
157168
def __init__(self, id, *args, **kwargs):
158169
self.id = str(id)
159-
for key, value in kwargs.iteritems():
170+
for key, value in six.iteritems(kwargs):
160171
setattr(self, key, value)
161172

162173
@classmethod
@@ -178,7 +189,7 @@ class User(ApiModel):
178189

179190
def __init__(self, id, *args, **kwargs):
180191
self.id = id
181-
for key, value in kwargs.iteritems():
192+
for key, value in six.iteritems(kwargs):
182193
setattr(self, key, value)
183194

184195
def __unicode__(self):

instagram/oauth2.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from json_import import simplejson
2-
import urllib
1+
from .json_import import simplejson
2+
from six.moves.urllib.parse import urlencode
33
from httplib2 import Http
44
import mimetypes
5+
import six
56

67

78
class OAuth2AuthExchangeError(Exception):
@@ -67,7 +68,7 @@ def _url_for_authorize(self, scope=None):
6768
}
6869
if scope:
6970
client_params.update(scope=' '.join(scope))
70-
url_params = urllib.urlencode(client_params)
71+
url_params = urlencode(client_params)
7172
return "%s?%s" % (self.api.authorize_url, url_params)
7273

7374
def _data_for_exchange(self, code=None, username=None, password=None, scope=None, user_id=None):
@@ -87,7 +88,7 @@ def _data_for_exchange(self, code=None, username=None, password=None, scope=None
8788
client_params.update(scope=' '.join(scope))
8889
elif user_id:
8990
client_params.update(user_id=user_id)
90-
return urllib.urlencode(client_params)
91+
return urlencode(client_params)
9192

9293
def get_authorize_url(self, scope=None):
9394
return self._url_for_authorize(scope=scope)
@@ -137,7 +138,7 @@ def _full_url_with_params(self, path, params, include_secret=False):
137138
return (self._full_url(path, include_secret) + self._full_query_with_params(params))
138139

139140
def _full_query_with_params(self, params):
140-
params = ("&" + urllib.urlencode(params)) if params else ""
141+
params = ("&" + urlencode(params)) if params else ""
141142
return params
142143

143144
def _auth_query(self, include_secret=False):
@@ -150,7 +151,7 @@ def _auth_query(self, include_secret=False):
150151
return base
151152

152153
def _post_body(self, params):
153-
return urllib.urlencode(params)
154+
return urlencode(params)
154155

155156
def _encode_multipart(params, files):
156157
boundary = "MuL7Ip4rt80uND4rYF0o"
@@ -208,5 +209,7 @@ def make_request(self, url, method="GET", body=None, headers=None):
208209
headers = headers or {}
209210
if not 'User-Agent' in headers:
210211
headers.update({"User-Agent": "%s Python Client" % self.api.api_name})
211-
http_obj = Http(disable_ssl_certificate_validation=True)
212+
# https://github.com/jcgregorio/httplib2/issues/173
213+
# bug in httplib2 w/ Python 3 and disable_ssl_certificate_validation=True
214+
http_obj = Http() if six.PY3 else Http(disable_ssl_certificate_validation=True)
212215
return http_obj.request(url, method, body=body, headers=headers)

instagram/subscriptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import hmac
22
import hashlib
3-
from json_import import simplejson
3+
from .json_import import simplejson
44

55
class SubscriptionType:
66
TAG = 'tag'

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
version="1.1.4",
66
description="Instagram API client",
77
license="MIT",
8-
install_requires=["simplejson","httplib2"],
8+
install_requires=["simplejson","httplib2","six"],
99
author="Instagram, Inc",
1010
author_email="apidevelopers@instagram.com",
1111
url="http://github.com/Instagram/python-instagram",

tests.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
#!/usr/bin/env python
22

33
import types
4+
import six
45
try:
56
import simplejson as json
67
except ImportError:
78
import json
89
import getpass
910
import unittest
10-
import urlparse
11+
from six.moves.urllib.parse import urlparse, parse_qs
1112
from instagram import client, oauth2, InstagramAPIError
1213

1314
TEST_AUTH = False
@@ -26,8 +27,8 @@ def request(self, url, method="GET", body=None, headers={}):
2627
'status':'400'
2728
}, "{}"
2829

29-
parsed = urlparse.urlparse(url)
30-
options = urlparse.parse_qs(parsed.query)
30+
parsed = urlparse(url)
31+
options = parse_qs(parsed.query)
3132

3233
fn_name = str(active_call)
3334
if fn_name == 'get_authorize_login_url':
@@ -43,6 +44,7 @@ def request(self, url, method="GET", body=None, headers={}):
4344

4445
fl = open('fixtures/%s.json' % fn_name)
4546
content = fl.read()
47+
fl.close()
4648
json_content = json.loads(content)
4749
status = json_content['meta']['code']
4850
return {
@@ -67,7 +69,7 @@ def setUp(self):
6769
def test_authorize_login_url(self):
6870
redirect_uri = self.unauthenticated_api.get_authorize_login_url()
6971
assert redirect_uri
70-
print "Please visit and authorize at:\n%s" % redirect_uri
72+
print("Please visit and authorize at:\n%s" % redirect_uri)
7173
code = raw_input("Paste received code (blank to skip): ").strip()
7274
if not code:
7375
return
@@ -129,7 +131,7 @@ def test_generator_user_feed(self):
129131
def test_generator_user_feed_all(self):
130132
generator = self.api.user_media_feed(as_generator=True, max_pages=None)
131133
for i in range(10):
132-
page = generator.next()
134+
page = six.advance_iterator(generator)
133135
str(generator)
134136

135137
generator = self.api.user_media_feed(as_generator=True, max_pages=0)
@@ -145,34 +147,34 @@ def test_user_recent_media(self):
145147
self.assertTrue( all( [hasattr(obj, 'type') for obj in media] ) )
146148

147149
image = media[0]
148-
self.assertEquals(
150+
self.assertEqual(
149151
image.get_standard_resolution_url(),
150152
"http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_7.jpg")
151153

152-
self.assertEquals(
154+
self.assertEqual(
153155
image.get_low_resolution_url(),
154156
"http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_6.jpg")
155157

156-
self.assertEquals(
158+
self.assertEqual(
157159
image.get_thumbnail_url(),
158160
"http://distillery-dev.s3.amazonaws.com/media/2011/02/02/1ce5f3f490a640ca9068e6000c91adc5_5.jpg")
159161

160-
self.assertEquals( False, hasattr(image, 'videos') )
162+
self.assertEqual( False, hasattr(image, 'videos') )
161163

162164
video = media[1]
163-
self.assertEquals(
165+
self.assertEqual(
164166
video.get_standard_resolution_url(),
165167
video.videos['standard_resolution'].url)
166168

167-
self.assertEquals(
169+
self.assertEqual(
168170
video.get_standard_resolution_url(),
169171
"http://distilleryvesper9-13.ak.instagram.com/090d06dad9cd11e2aa0912313817975d_101.mp4")
170172

171-
self.assertEquals(
173+
self.assertEqual(
172174
video.get_low_resolution_url(),
173175
"http://distilleryvesper9-13.ak.instagram.com/090d06dad9cd11e2aa0912313817975d_102.mp4")
174176

175-
self.assertEquals(
177+
self.assertEqual(
176178
video.get_thumbnail_url(),
177179
"http://distilleryimage2.ak.instagram.com/11f75f1cd9cc11e2a0fd22000aa8039a_5.jpg")
178180

0 commit comments

Comments
 (0)