Skip to content

Commit 9fdc0d4

Browse files
committed
V0.2
New API: api.photos_favorites api.photos_votes api.users_search api.comments_post() Bug Fix: #3
1 parent f962a19 commit 9fdc0d4

File tree

17 files changed

+611
-348
lines changed

17 files changed

+611
-348
lines changed

README.md

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,53 +10,56 @@ this library was inspired by tweepy(https://github.com/tweepy/tweepy), python-in
1010
pip install python-500px
1111

1212
## Requires
13-
* httplib2
1413
* simplejson
1514

1615
## Usage
17-
1816
from fivehundredpx.client import FiveHundredPXAPI
1917
from fivehundredpx.auth import *
20-
18+
2119
unauthorized_api = FiveHundredPXAPI(handler)
20+
unauthorized_api.users_show(consumer_key=CONSUMER_KEY, id='727199')
2221

23-
handler = OAuthHandler(CONSUMER_KEY,CONSUMER_SECRET)
24-
handler.set_access_token(OAUTH_TOKEN,OAUTH_TOKEN_SECRET)
22+
handler = OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
23+
handler.set_access_token(OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
2524
api = FiveHundredPXAPI(handler)
2625
api.users()
2726

2827
## Authentication
29-
30-
- ** initialize **
31-
handler = OAuthHandler(CONSUMER_KEY,CONSUMER_SECRET)
32-
33-
- **handler.get_authorization_url()** - get a url for authorization. **[authorize][]**
34-
- **handler.get_access_token(verifier)** - get an access token. **[access_token][]**
35-
token = handler.get_access_token(verifier)
36-
print "this is your access token:\n%s" % token.key
37-
print "this is your access token secret:\n%s" % token.secret
28+
please check 500px's authentication document(https://github.com/500px/api-documentation/tree/master/authentication)
29+
tests/oauth.py shows how to get request/access token.
3830

39-
- **handler.get_xauth_access_token(username,password)** - get an access token by using xAuth.
31+
verifier:
32+
self.handler.get_authorization_url() # go to this url and get verifier
33+
token = self.handler.get_access_token(verifier)
34+
token.key, token.secret
4035

36+
xauth:
37+
token = self.handler.get_request_token()
38+
self.handler.set_request_token(token.key, token.secret)
39+
token = self.handler.get_xauth_access_token(username, password)
40+
token.key, token.secret
4141

4242
## Methods
4343

4444
* api.photos()
4545
* api.photos_search()
4646
* api.photos_id()
4747
* api.photos_post()
48+
* api.photos_update()
4849
* api.photos_delete()
4950
* api.photos_comments()
5051
* api.photos_comments_post()
52+
* api.photos_favorites()
5153
* api.photos_favorite_post()
5254
* api.photos_favorite_delete()
5355
* api.photos_tags_post()
5456
* api.photos_tags_delete()
57+
* api.photos_votes()
5558
* api.photos_vote_post()
5659
* api.upload_photo()
57-
* api.photos_update()
5860
* api.users()
5961
* api.users_show()
62+
* api.users_search()
6063
* api.users_friends()
6164
* api.users_followers()
6265
* api.users_friends_post()
@@ -68,15 +71,22 @@ this library was inspired by tweepy(https://github.com/tweepy/tweepy), python-in
6871
* api.blogs_post()
6972
* api.blogs_delete()
7073
* api.blogs_update()
74+
* api.comments_post()
7175
* api.collections()
7276
* api.collections_id()
7377
* api.collections_post()
7478
* api.collections_update()
7579
* api.collections_delete()
7680

77-
please check test.py
78-
81+
## Test
82+
python tests/oauth.py [cunsumer_key] [consumer_secret]
83+
python tests/blog.py [cunsumer_key] [consumer_secret] [oauth_token] [oauth_token_secret]
84+
python tests/collection.py [cunsumer_key] [consumer_secret] [oauth_token] [oauth_token_secret]
85+
python tests/user.py [cunsumer_key] [consumer_secret] [oauth_token] [oauth_token_secret]
86+
python tests/photo.py [cunsumer_key] [consumer_secret] [oauth_token] [oauth_token_secret]
87+
python tests/upload.py [cunsumer_key] [consumer_secret] [oauth_token] [oauth_token_secret]
7988

89+
[authentication]: https://github.com/500px/api-documentation/tree/master/authentication
8090
[authorize]: https://github.com/500px/api-documentation/blob/master/authentication/POST_oauth_authorize.md
8191
[request_token]: https://github.com/500px/api-documentation/blob/master/authentication/POST_oauth_requesttoken.md
8292
[access_token]: https://github.com/500px/api-documentation/blob/master/authentication/POST_oauth_accesstoken.md

fivehundredpx/auth.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def apply_auth(self,url,method,headers,parameters):
4545
)
4646
request.sign_request(self._sigmethod,self._consumer,self.access_token)
4747
headers.update(request.to_header())
48+
return request
4849

4950
def get_authorization_url(self):
5051
try:

fivehundredpx/bind.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from fivehundredpx.utils import encode_string
1+
from fivehundredpx.utils import Util
22
from fivehundredpx.errors import *
33
import urllib, re, httplib, time, simplejson
44

@@ -14,59 +14,78 @@ class APIMethod(object):
1414
model_class = config.get('model_class',None)
1515
response_type = config.get('response_type','list')
1616
require_auth = config.get('require_auth',False)
17+
as_query = config.get('as_query',False)
1718

18-
def __init__(self,api,*args,**kwargs):
19+
def __init__(self, api, *args, **kwargs):
1920
'''parameters for every request'''
2021
if self.require_auth and not api.auth_handler: raise FiveHundredClientError('auth handler is required')
21-
22+
2223
self.api = api
2324
self.parameters = {}
2425
self.as_generator = kwargs.pop("as_generator", False)
2526
self.max_pages = kwargs.pop("max_pages", 3)
2627
self.headers = kwargs.pop('headers', {})
27-
self.body = kwargs.pop('http_body', None)
28+
self.body = kwargs.pop('http_body', {})
2829
self.protocol = 'https://' if self.api.secure else 'http://'
2930
if 'method' in kwargs: self.method = kwargs.pop('method')
3031
if 'require_auth' in kwargs: self.require_auth = kwargs.pop('require_auth')
3132
self._build_parameters(args, kwargs)
3233
self._build_path()
3334

34-
def _build_parameters(self,args,kwargs):
35+
def _build_parameters(self, args, kwargs):
3536
for index,value in enumerate(args):
3637
if value is None: continue
3738
try:
38-
self.parameters[self.allowed_params[index]] = encode_string(value)
39+
self.parameters[self.allowed_params[index]] = value
3940
except IndexError:
4041
raise FiveHundredsClientError("Too many arguments supplied")
4142

4243
for key,value in kwargs.iteritems():
4344
if value is None: continue
44-
self.parameters[key] = encode_string(value)
45-
45+
if type(value) in (list, tuple):
46+
self.parameters[key + "[]"] = value
47+
else:
48+
self.parameters[key] = value
49+
4650
def _build_path(self):
4751
for variable in re_path_template.findall(self.path):
4852
name = variable.strip('{}')
4953
try:
50-
value = urllib.quote(self.parameters[name])
54+
value = urllib.quote(str(self.parameters[name]))
5155
del self.parameters[name]
5256
self.path = self.path.replace(variable,value)
5357
except KeyError:
5458
raise FiveHundredsClientError('No parameter value found for path variable: %s' % name)
5559

5660
def _execute(self):
61+
url = "%s%s%s%s" % (self.protocol,self.api.host,self.api.version,self.path)
62+
63+
if self.method == "GET" or self.as_query == True:
64+
param_list = []
65+
for key, value in self.parameters.iteritems():
66+
if type(value) in (list, tuple):
67+
for v in value:
68+
param_list.append("%s=%s" % (key, v))
69+
else:
70+
param_list.append("%s=%s" % (key, Util.replace_space(value)))
71+
72+
if len(param_list) != 0 : url = "%s?%s" % (url, "&".join(param_list))
73+
74+
elif self.method == "POST" or self.method == "PUT":
75+
if self.headers.has_key('Content-Type') == False:
76+
self.headers['Content-Type'] = "application/x-www-form-urlencoded; charset=UTF-8"
5777

58-
url = "%s%s%s%s" % (self.protocol,self.api.host,self.api.version,self.path)
59-
params = urllib.urlencode(self.parameters) if len(self.parameters) != 0 else ''
78+
postdata = None
79+
if self.require_auth and self.api.auth_handler:
80+
request = self.api.auth_handler.apply_auth(url, self.method, self.headers, self.parameters)
81+
if not self.method == "GET": postdata = request.to_postdata()
6082

61-
if self.require_auth and self.api.auth_handler:
62-
self.api.auth_handler.apply_auth(url, self.method, self.headers, self.parameters)
83+
if self.path == "/upload": postdata = self.body
6384

64-
if not params == '': url = "%s?%s" % (url,params)
65-
6685
for count in xrange(self.api.retry_count):
6786
conn = httplib.HTTPSConnection(self.api.host) if self.api.secure else httplib.HTTPConnection(self.api.host)
6887
try:
69-
conn.request(self.method, url, body=self.body, headers=self.headers)
88+
conn.request(self.method, url, body=postdata, headers=self.headers)
7089
response = conn.getresponse()
7190
except Exception, e:
7291
conn.close()

fivehundredpx/client.py

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from fivehundredpx import settings
22
from fivehundredpx.auth import *
33
from fivehundredpx.bind import bind_api
4-
from fivehundredpx.utils import *
4+
from fivehundredpx.utils import FileUtil
55

66
class FiveHundredPXAPI(object):
77

@@ -15,61 +15,60 @@ def __init__(self,auth_handler=None,host=None,secure=True,version=None,retry_cou
1515
self.retry_delay = retry_delay or settings.RETRY_DELAY
1616
self.retry_errors = retry_errors
1717

18+
#### Photo API
19+
# https://github.com/500px/api-documentation/tree/master/endpoints/photo
1820
photos = bind_api(path='/photos')
19-
photos_search = bind_api(path='/photos/search')
21+
photos_search = bind_api(path='/photos/search', require_auth=True)
2022
photos_id = bind_api(path='/photos/{id}', allowed_params=['id'])
2123
photos_post = bind_api(path='/photos', method='POST', require_auth=True)
24+
photos_update = bind_api(path='/photos/{id}', method='PUT', require_auth=True, as_query=False)
2225
photos_delete = bind_api(path='/photos/{id}', method='DELETE', allowed_params=['id'],require_auth=True)
2326
photos_comments = bind_api(path='/photos/{id}/comments', allowed_params=['id'])
2427
photos_comments_post = bind_api(path='/photos/{id}/comments', method='POST', allowed_params=['id'], require_auth=True)
28+
photos_favorites = bind_api(path='/photos/{id}/favorites', allowed_params=['id'], require_auth=True)
2529
photos_favorite_post = bind_api(path='/photos/{id}/favorite', method='POST', allowed_params=['id'], require_auth=True)
2630
photos_favorite_delete = bind_api(path='/photos/{id}/favorite', method='DELETE', allowed_params=['id'], require_auth=True)
2731
photos_tags_post = bind_api(path='/photos/{id}/tags', method='POST', allowed_params=['id'], require_auth=True)
28-
photos_tags_delete = bind_api(path='/photos/{id}/tags', method='DELETE', allowed_params=['id'], require_auth=True)
29-
photos_vote_post = bind_api(path='/photos/{id}/vote', method='POST', allowed_params=['id'], require_auth=True)
32+
photos_tags_delete = bind_api(path='/photos/{id}/tags', method='DELETE', allowed_params=['id'], require_auth=True, as_query=True)
33+
photos_votes = bind_api(path='/photos/{id}/votes', allowed_params=['id'], require_auth=True)
34+
photos_vote_post = bind_api(path='/photos/{id}/vote', method='POST', allowed_params=['id'], require_auth=True, as_query=True)
35+
photos_report = bind_api(path='/photos/{id}/report', method='POST', allowed_params=['id'], require_auth=True)
3036

3137
def upload_photo(self, filename=None,fp=None,file_type=None, **kwargs):
32-
headers,body = create_body_by_filepath(filename,'file',kwargs) if fp==None else create_body_by_fp(fp, 'file', file_type, kwargs)
38+
headers,body = FileUtil.create_body_by_filepath(filename,'file',kwargs) if fp==None else FileUtil.create_body_by_fp(fp, 'file', file_type, kwargs)
3339
return bind_api(
3440
path = '/upload',
35-
method = 'POST',
36-
require_auth = True
41+
method = 'POST'
3742
)(self,http_body=body, headers=headers)
3843

39-
def photos_update(self, id, **kwargs):
40-
headers,body = create_body(kwargs)
41-
return bind_api(
42-
path='/photos/{id}',
43-
method = 'PUT',
44-
allowed_params=['id'],
45-
require_auth = True
46-
)(self,id=id, http_body=body, headers=headers)
47-
44+
#### User API
45+
# https://github.com/500px/api-documentation/tree/master/endpoints/user
4846
users = bind_api(path='/users', require_auth=True)
4947
users_show = bind_api(path='/users/show')
48+
users_search = bind_api(path='/users/search')
5049
users_friends = bind_api(path='/users/{id}/friends', allowed_params=['id'])
5150
users_followers = bind_api(path='/users/{id}/followers', allowed_params=['id'])
5251
users_friends_post = bind_api(path='/users/{id}/friends', method='POST', allowed_params=['id'])
5352
users_friends_delete = bind_api(path='/users/{id}/friends', method='DELETE', allowed_params=['id'])
5453

54+
#### Blog API
55+
# https://github.com/500px/api-documentation/tree/master/endpoints/blog
5556
blogs = bind_api(path='/blogs')
5657
blogs_id = bind_api(path='/blogs/{id}', allowed_params=['id'])
5758
blogs_comments = bind_api(path='/blogs/{id}/comments', allowed_params=['id'])
5859
blogs_comments_post = bind_api(path='/blogs/{id}/comments', require_auth=True, allowed_params=['id'], method='POST')
5960
blogs_post = bind_api(path='/blogs', require_auth=True, method='POST')
61+
blogs_update = bind_api(path='/blogs/{id}', require_auth=True, allowed_params=['id'], method='PUT')
6062
blogs_delete = bind_api(path='/blogs/{id}', require_auth=True, allowed_params=['id'], method='DELETE')
63+
64+
#### Comment API
65+
# https://github.com/500px/api-documentation/tree/master/endpoints/comments
66+
comments_post = bind_api(path='/comments/{id}/comments', require_auth=True, allowed_params=['id'], method='POST')
6167

62-
def blogs_update(self, id, **kwargs):
63-
headers,body = create_body(kwargs)
64-
return bind_api(
65-
path='/blogs/{id}',
66-
method = 'PUT',
67-
allowed_params=['id'],
68-
require_auth = True
69-
)(self,id=id, http_body=body, headers=headers)
70-
71-
collections = bind_api(path='/collections')
72-
collections_id = bind_api(path='/collections/{id}', allowed_params=['id'])
73-
collections_post = bind_api(path='/collections', require_auth=True, method='POST')
74-
collections_update = bind_api(path='/collections/{id}', require_auth=True, method='POST', allowed_params=['id'])
75-
collections_delete = bind_api(path='/collections/{id}', require_auth=True, method='DELETE', allowed_params=['id'])
68+
#### Collection API
69+
# https://github.com/500px/api-documentation/tree/master/endpoints/collections
70+
collections = bind_api(path='/collections', require_auth=True)
71+
collections_id = bind_api(path='/collections/{id}', require_auth=True, allowed_params=['id'])
72+
collections_post = bind_api(path='/collections', require_auth=True, method='POST', as_query=True)
73+
collections_update = bind_api(path='/collections/{id}', require_auth=True, method='PUT', allowed_params=['id'], as_query=True)
74+
collections_delete = bind_api(path='/collections/{id}', require_auth=True, method='DELETE', allowed_params=['id'], as_query=True)

fivehundredpx/oauth.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,11 @@ def to_header(self, realm=''):
211211

212212
def to_postdata(self):
213213
"""Serialize as post data for a POST request."""
214-
return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
214+
# return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
215+
# for k, v in self.parameters.iteritems()])
216+
return '&'.join(['%s=%s' % (escape(str(k)), str(v).replace(" ", "+")) \
215217
for k, v in self.parameters.iteritems()])
216-
218+
217219
def to_url(self):
218220
"""Serialize as a URL for a GET request."""
219221
return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
@@ -227,8 +229,17 @@ def get_normalized_parameters(self):
227229
except:
228230
pass
229231
# Escape key values before sorting.
230-
key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
231-
for k,v in params.items()]
232+
# key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
233+
# for k,v in params.items()]
234+
235+
# for this param: image_size[]=2&image_size[]=3
236+
key_values = []
237+
for k,v in params.items():
238+
if type(v) in (list, tuple):
239+
[ key_values.append( (escape(_utf8_str(k)), escape(_utf8_str(_v)) )) for _v in v ]
240+
else:
241+
key_values.append( (escape(_utf8_str(k)), escape(_utf8_str(v)) ) )
242+
232243
# Sort lexicographically, first after key, then after value.
233244
key_values.sort()
234245
# Combine key value pairs into a string.

0 commit comments

Comments
 (0)