Skip to content

Commit e4c81a2

Browse files
committed
v0.1.0
0 parents  commit e4c81a2

File tree

13 files changed

+1314
-0
lines changed

13 files changed

+1314
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.pyc
2+
.DS_Store
3+
*.swp

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
python-500px
2+
======
3+
A Python client for the 500px API
4+
5+
Installation
6+
----
7+
pip install python-500px
8+
9+
Requires
10+
----
11+
* httplib2
12+
* simplejson
13+
14+
Usage
15+
----
16+
from fivehundredpx.client import FiveHundredPXAPI
17+
from fivehundredpx.auth import *
18+
19+
unauthorized_api = FiveHundredPXAPI(handler)
20+
21+
handler = OAuthHandler(CONSUMER_KEY,CONSUMER_SECRET)
22+
handler.set_access_token(OAUTH_TOKEN,OAUTH_TOKEN_SECRET)
23+
api = FiveHundredPXAPI(handler)
24+
api.users()
25+
26+
Methods
27+
----
28+
* api.photos()
29+
* api.photos_search()
30+
* api.photos_id()
31+
* api.photos_post()
32+
* api.photos_delete()
33+
* api.photos_comments()
34+
* api.photos_comments_post()
35+
* api.photos_favorite_post()
36+
* api.photos_favorite_delete()
37+
* api.photos_tags_post()
38+
* api.photos_tags_delete()
39+
* api.photos_vote_post()
40+
* api.upload_photo()
41+
* api.photos_update()
42+
43+
* api.users()
44+
* api.users_show()
45+
* api.users_friends()
46+
* api.users_followers()
47+
* api.users_friends_post()
48+
* api.users_friends_delete()
49+
50+
* api.blogs()
51+
* api.blogs_id()
52+
* api.blogs_comments()
53+
* api.blogs_comments_post()
54+
* api.blogs_post()
55+
* api.blogs_delete()
56+
* api.blogs_update()
57+
58+
* api.collections()
59+
* api.collections_id()
60+
* api.collections_post()
61+
* api.collections_update()
62+
* api.collections_delete()
63+
64+
please check test.py
65+

fivehundredpx/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from fivehundredpx.client import FiveHundredPXAPI
2+
from fivehundredpx.auth import *
3+
from fivehundredpx.errors import *
4+
5+
"""
6+
500px API library
7+
"""
8+
__version__ = '0.1.0'
9+
__author__ = 'Akira Hirakawa'
10+
__license__ = 'MIT'

fivehundredpx/auth.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from urllib2 import Request, urlopen
2+
3+
from fivehundredpx import oauth
4+
from fivehundredpx.settings import *
5+
from fivehundredpx.errors import *
6+
7+
class OAuthHandler(object):
8+
9+
def __init__(self,consumer_key,consumer_secret,callback=None,secure=False):
10+
self._consumer = oauth.OAuthConsumer(consumer_key,consumer_secret)
11+
self._sigmethod = oauth.OAuthSignatureMethod_HMAC_SHA1()
12+
self.request_token = None
13+
self.access_token = None
14+
self.callback = callback
15+
self.username = None
16+
self.secure = secure
17+
self.host = API_HOST
18+
self.version = API_VERSION
19+
self.root = OAUTH_ROOT
20+
21+
def _get_oauth_url(self,endpoint,secure=True):
22+
prefix = 'https://' if (self.secure or secure) else 'http://'
23+
return prefix + self.host + self.version + self.root + endpoint
24+
25+
def set_request_token(self, key, secret):
26+
self.request_token = oauth.OAuthToken(key, secret)
27+
28+
def set_access_token(self, key, secret):
29+
self.access_token = oauth.OAuthToken(key, secret)
30+
31+
def _get_request_token(self):
32+
url = self._get_oauth_url('request_token')
33+
request = oauth.OAuthRequest.from_consumer_and_token(self._consumer, http_url=url, callback=self.callback)
34+
request.sign_request(self._sigmethod,self._consumer,None)
35+
response = urlopen(Request(url,headers=request.to_header()))
36+
return oauth.OAuthToken.from_string(response.read())
37+
38+
def apply_auth(self,url,method,headers,parameters):
39+
request = oauth.OAuthRequest.from_consumer_and_token(
40+
self._consumer,
41+
http_url = url,
42+
http_method = method,
43+
token = self.access_token,
44+
parameters = parameters
45+
)
46+
request.sign_request(self._sigmethod,self._consumer,self.access_token)
47+
headers.update(request.to_header())
48+
49+
def get_authorization_url(self):
50+
try:
51+
self.request_token = self._get_request_token()
52+
request = oauth.OAuthRequest.from_token_and_callback(
53+
token=self.request_token, http_url=self._get_oauth_url('authorize')
54+
)
55+
return request.to_url()
56+
except Exception, e:
57+
raise FiveHundredClientError(e)
58+
59+
def get_access_token(self,verifier=None):
60+
try:
61+
url = self._get_oauth_url('access_token')
62+
request = oauth.OAuthRequest.from_consumer_and_token(
63+
self._consumer, token=self.request_token, http_url=url, verifier=str(verifier)
64+
)
65+
request.sign_request(self._sigmethod, self._consumer, self.request_token)
66+
response = urlopen(Request(url,headers=request.to_header()))
67+
self.access_token = oauth.OAuthToken.from_string(response.read())
68+
return self.access_token
69+
except Exception, e:
70+
raise FiveHundredClientError(e)
71+
72+
def get_xauth_access_token(self,username,password):
73+
try:
74+
url = self._get_oauth_url('access_token',secure=True)
75+
request = oauth.OAuthRequest.from_consumer_and_token(
76+
oauth_consumer=self._consumer,
77+
http_method='POST',
78+
http_url=url,
79+
parameters={
80+
'x_auth_mode': 'client_auth',
81+
'x_auth_username': username,
82+
'x_auth_password': password
83+
}
84+
)
85+
request.sign_request(self._sigmethod,self._consumer,None)
86+
response = urlopen(Request(url,data=request.to_postdata()))
87+
self.access_token = oauth.OAuthToken.from_string(response.read())
88+
return self.access_token
89+
except Exception, e:
90+
raise FiveHundredClientError(e)

fivehundredpx/bind.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from fivehundredpx.utils import encode_string
2+
from fivehundredpx.errors import *
3+
import urllib, re, httplib, time, simplejson
4+
5+
re_path_template = re.compile('{\w+}')
6+
7+
def bind_api(**config):
8+
9+
class APIMethod(object):
10+
'''common parameters for a api action'''
11+
path = config['path']
12+
method = config.get('method','GET')
13+
allowed_params = config.get('allowed_params',[])
14+
model_class = config.get('model_class',None)
15+
response_type = config.get('response_type','list')
16+
require_auth = config.get('require_auth',False)
17+
18+
def __init__(self,api,*args,**kwargs):
19+
'''parameters for every request'''
20+
if self.require_auth and not api.auth_handler: raise FiveHundredClientError('auth handler is required')
21+
22+
self.api = api
23+
self.parameters = {}
24+
self.as_generator = kwargs.pop("as_generator", False)
25+
self.max_pages = kwargs.pop("max_pages", 3)
26+
self.headers = kwargs.pop('headers', {})
27+
self.body = kwargs.pop('http_body', None)
28+
self.protocol = 'https://' if self.api.secure else 'http://'
29+
if 'method' in kwargs: self.method = kwargs.pop('method')
30+
if 'require_auth' in kwargs: self.require_auth = kwargs.pop('require_auth')
31+
self._build_parameters(args, kwargs)
32+
self._build_path()
33+
34+
def _build_parameters(self,args,kwargs):
35+
for index,value in enumerate(args):
36+
if value is None: continue
37+
try:
38+
self.parameters[self.allowed_params[index]] = encode_string(value)
39+
except IndexError:
40+
raise FiveHundredsClientError("Too many arguments supplied")
41+
42+
for key,value in kwargs.iteritems():
43+
if value is None: continue
44+
self.parameters[key] = encode_string(value)
45+
46+
def _build_path(self):
47+
for variable in re_path_template.findall(self.path):
48+
name = variable.strip('{}')
49+
try:
50+
value = urllib.quote(self.parameters[name])
51+
del self.parameters[name]
52+
self.path = self.path.replace(variable,value)
53+
except KeyError:
54+
raise FiveHundredsClientError('No parameter value found for path variable: %s' % name)
55+
56+
def _execute(self):
57+
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 ''
60+
61+
if self.require_auth and self.api.auth_handler:
62+
self.api.auth_handler.apply_auth(url, self.method, self.headers, self.parameters)
63+
64+
if not params == '': url = "%s?%s" % (url,params)
65+
66+
for count in xrange(self.api.retry_count):
67+
conn = httplib.HTTPSConnection(self.api.host) if self.api.secure else httplib.HTTPConnection(self.api.host)
68+
try:
69+
conn.request(self.method, url, body=self.body, headers=self.headers)
70+
response = conn.getresponse()
71+
except Exception, e:
72+
conn.close()
73+
raise FiveHundredClientError('Failed to send request: %s' % e)
74+
75+
if self.api.retry_errors:
76+
if response.status not in self.api.retry_errors: break
77+
else:
78+
if response.status == 200: break
79+
conn.close()
80+
time.sleep(self.api.retry_delay)
81+
82+
if response.status > 199 and response.status < 300:
83+
result = response.read()
84+
conn.close()
85+
return simplejson.loads(result)
86+
else:
87+
try:
88+
error_msg = self.api.parser.parse_error(response.read())
89+
except Exception:
90+
error_msg = "500PX error response: status code = %s" % response.status
91+
finally:
92+
conn.close()
93+
raise FiveHundredClientError(error_msg,status=response.status)
94+
95+
def _generator(self):
96+
base = self.parameters['page'] if 'page' in self.parameters else 1
97+
for count in xrange(self.max_pages):
98+
self.parameters['page'] = base + count
99+
yield self._execute()
100+
return
101+
102+
def execute(self):
103+
return self._generator() if self.as_generator else self._execute()
104+
105+
def _call(api, *args, **kwargs):
106+
method = APIMethod(api, *args, **kwargs)
107+
return method.execute()
108+
109+
return _call

fivehundredpx/client.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from fivehundredpx import settings
2+
from fivehundredpx.auth import *
3+
from fivehundredpx.bind import bind_api
4+
from fivehundredpx.utils import *
5+
6+
class FiveHundredPXAPI(object):
7+
8+
def __init__(self,auth_handler=None,host=None,secure=True,version=None,retry_count=None,retry_delay=None,retry_errors=None):
9+
self.format = 'json'
10+
self.auth_handler = auth_handler
11+
self.secure = secure
12+
self.host = host or settings.API_HOST
13+
self.version = version or settings.API_VERSION
14+
self.retry_count = retry_count or settings.RETRY_COUNT
15+
self.retry_delay = retry_delay or settings.RETRY_DELAY
16+
self.retry_errors = retry_errors
17+
18+
photos = bind_api(path='/photos')
19+
photos_search = bind_api(path='/photos/search')
20+
photos_id = bind_api(path='/photos/{id}', allowed_params=['id'])
21+
photos_post = bind_api(path='/photos', method='POST', require_auth=True)
22+
photos_delete = bind_api(path='/photos/{id}', method='DELETE', allowed_params=['id'],require_auth=True)
23+
photos_comments = bind_api(path='/photos/{id}/comments', allowed_params=['id'])
24+
photos_comments_post = bind_api(path='/photos/{id}/comments', method='POST', allowed_params=['id'], require_auth=True)
25+
photos_favorite_post = bind_api(path='/photos/{id}/favorite', method='POST', allowed_params=['id'], require_auth=True)
26+
photos_favorite_delete = bind_api(path='/photos/{id}/favorite', method='DELETE', allowed_params=['id'], require_auth=True)
27+
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)
30+
31+
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)
33+
return bind_api(
34+
path = '/upload',
35+
method = 'POST',
36+
require_auth = True
37+
)(self,http_body=body, headers=headers)
38+
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+
48+
users = bind_api(path='/users', require_auth=True)
49+
users_show = bind_api(path='/users/show')
50+
users_friends = bind_api(path='/users/{id}/friends', allowed_params=['id'])
51+
users_followers = bind_api(path='/users/{id}/followers', allowed_params=['id'])
52+
users_friends_post = bind_api(path='/users/{id}/friends', method='POST', allowed_params=['id'])
53+
users_friends_delete = bind_api(path='/users/{id}/friends', method='DELETE', allowed_params=['id'])
54+
55+
blogs = bind_api(path='/blogs')
56+
blogs_id = bind_api(path='/blogs/{id}', allowed_params=['id'])
57+
blogs_comments = bind_api(path='/blogs/{id}/comments', allowed_params=['id'])
58+
blogs_comments_post = bind_api(path='/blogs/{id}/comments', require_auth=True, allowed_params=['id'], method='POST')
59+
blogs_post = bind_api(path='/blogs', require_auth=True, method='POST')
60+
blogs_delete = bind_api(path='/blogs/{id}', require_auth=True, allowed_params=['id'], method='DELETE')
61+
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'])

fivehundredpx/errors.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class FiveHundredClientError(Exception):
2+
3+
def __init__(self,error_message,status=None):
4+
self.error_message = error_message
5+
self.status = status
6+
7+
def __str__(self):
8+
return self.error_message

0 commit comments

Comments
 (0)