Extended API to add pythonic classes/methods. See the updated README.markdown for examples
This commit is contained in:
parent
ccc6e6ba1c
commit
b418cf7e78
9 changed files with 457 additions and 60 deletions
|
@ -5,7 +5,7 @@ Open Photo API / Python Library
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
<a name="install"></a>
|
<a name="install"></a>
|
||||||
### Installation
|
### Installation
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
@ -14,10 +14,26 @@ python setup.py install
|
||||||
|
|
||||||
To use the library you need to first ``import openphoto``, then instantiate an instance of the class and start making calls.
|
To use the library you need to first ``import openphoto``, then instantiate an instance of the class and start making calls.
|
||||||
|
|
||||||
|
You can use the library in one of two ways:
|
||||||
|
* Direct GET/POST calls to the server
|
||||||
|
* Access via Python classes/methods
|
||||||
|
|
||||||
|
<a name="get_post"></a>
|
||||||
|
### Direct GET/POST:
|
||||||
|
|
||||||
from openphoto import OpenPhoto
|
from openphoto import OpenPhoto
|
||||||
client = OpenPhoto(host, consumerKey, consumerSecret, token, tokenSecret)
|
client = OpenPhoto(host, consumerKey, consumerSecret, token, tokenSecret)
|
||||||
resp = client.get('/photos/list.json')
|
resp = client.get("/photos/list.json")
|
||||||
resp = client.post('/photo/62/update.json', {'tags': 'tag1,tag2'})
|
resp = client.post("/photo/62/update.json", tags=["tag1", "tag2"])
|
||||||
|
|
||||||
|
<a name="python_classes"></a>
|
||||||
|
### Python classes/methods
|
||||||
|
|
||||||
|
from openphoto import OpenPhoto
|
||||||
|
client = OpenPhoto(host, consumerKey, consumerSecret, token, tokenSecret)
|
||||||
|
photos = client.photos_list()
|
||||||
|
photos[0].update(tags=["tag1", "tag2"])
|
||||||
|
print photos[0].tags
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,50 +1,8 @@
|
||||||
import oauth2 as oauth
|
from openphoto_http import OpenPhotoHttp, OpenPhotoError, OpenPhotoDuplicateError
|
||||||
import urlparse
|
from api_photo import ApiPhoto
|
||||||
import urllib
|
from api_tag import ApiTag
|
||||||
import httplib2
|
from api_album import ApiAlbum
|
||||||
import types
|
|
||||||
|
|
||||||
|
class OpenPhoto(OpenPhotoHttp, ApiPhoto, ApiTag, ApiAlbum):
|
||||||
class OpenPhoto(object):
|
|
||||||
""" Client library for OpenPhoto """
|
""" Client library for OpenPhoto """
|
||||||
|
pass
|
||||||
def __init__(self, host, consumer_key='', consumer_secret='',
|
|
||||||
token='', token_secret=''):
|
|
||||||
self.host = host
|
|
||||||
self.consumer_key = consumer_key
|
|
||||||
self.consumer_secret = consumer_secret
|
|
||||||
self.token = token
|
|
||||||
self.token_secret = token_secret
|
|
||||||
|
|
||||||
def get(self, endpoint, params={}):
|
|
||||||
url = urlparse.urlunparse(('http', self.host, endpoint, '',
|
|
||||||
urllib.urlencode(params), ''))
|
|
||||||
if self.consumer_key:
|
|
||||||
consumer = oauth.Consumer(self.consumer_key, self.consumer_secret)
|
|
||||||
token = oauth.Token(self.token, self.token_secret)
|
|
||||||
|
|
||||||
client = oauth.Client(consumer, token)
|
|
||||||
|
|
||||||
else:
|
|
||||||
client = httplib2.Http()
|
|
||||||
|
|
||||||
headers, content = client.request(url, "GET")
|
|
||||||
return content
|
|
||||||
|
|
||||||
def post(self, endpoint, params={}):
|
|
||||||
url = urlparse.urlunparse(('http', self.host, endpoint, '', '', ''))
|
|
||||||
|
|
||||||
if self.consumer_key:
|
|
||||||
consumer = oauth.Consumer(self.consumer_key, self.consumer_secret)
|
|
||||||
token = oauth.Token(self.token, self.token_secret)
|
|
||||||
|
|
||||||
# ensure utf-8 encoding for all values.
|
|
||||||
params = dict([(k, v.encode('utf-8')
|
|
||||||
if type(v) is types.UnicodeType else v)
|
|
||||||
for (k, v) in params.items()])
|
|
||||||
|
|
||||||
client = oauth.Client(consumer, token)
|
|
||||||
body = urllib.urlencode(params)
|
|
||||||
headers, content = client.request(url, "POST", body)
|
|
||||||
|
|
||||||
return content
|
|
||||||
|
|
33
openphoto/api_album.py
Normal file
33
openphoto/api_album.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
from openphoto_http import OpenPhotoHttp, OpenPhotoError
|
||||||
|
from objects import Album
|
||||||
|
|
||||||
|
class ApiAlbum(OpenPhotoHttp):
|
||||||
|
def album_create(self, name, **kwds):
|
||||||
|
""" Create a new album and return it"""
|
||||||
|
result = self.post("/album/create.json", name=name, **kwds)["result"]
|
||||||
|
return Album(self, result)
|
||||||
|
|
||||||
|
def album_delete(self, album_id, **kwds):
|
||||||
|
""" Delete an album """
|
||||||
|
album = Album(self, {"id": album_id})
|
||||||
|
album.delete(**kwds)
|
||||||
|
|
||||||
|
def album_form(self, album_id, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def album_add_photos(self, album_id, photo_ids, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def album_remove_photos(self, album_id, photo_ids, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def albums_list(self, **kwds):
|
||||||
|
""" Return a list of Album objects """
|
||||||
|
results = self.get("/albums/list.json", **kwds)["result"]
|
||||||
|
return [Album(self, album) for album in results]
|
||||||
|
|
||||||
|
def album_update(self, album_id, **kwds):
|
||||||
|
""" Update an album """
|
||||||
|
album = Album(self, {"id": album_id})
|
||||||
|
album.update(**kwds)
|
||||||
|
# Don't return the album, since the API doesn't give us the modified album
|
81
openphoto/api_photo.py
Normal file
81
openphoto/api_photo.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from openphoto_http import OpenPhotoHttp, OpenPhotoError
|
||||||
|
from objects import Photo
|
||||||
|
|
||||||
|
class ApiPhoto(OpenPhotoHttp):
|
||||||
|
def photo_delete(self, photo_id, **kwds):
|
||||||
|
""" Delete a photo """
|
||||||
|
photo = Photo(self, {"id": photo_id})
|
||||||
|
photo.delete(**kwds)
|
||||||
|
|
||||||
|
def photo_edit(self, photo_id, **kwds):
|
||||||
|
""" Returns an HTML form to edit a photo """
|
||||||
|
photo = Photo(self, {"id": photo_id})
|
||||||
|
return photo.edit(**kwds)
|
||||||
|
|
||||||
|
def photo_replace(self, photo_id, photo_file, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def photo_replace_encoded(self, photo_id, photo_file, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def photo_update(self, photo_id, **kwds):
|
||||||
|
"""
|
||||||
|
Update a photo with the specified parameters.
|
||||||
|
Returns the updated photo object
|
||||||
|
"""
|
||||||
|
photo = Photo(self, {"id": photo_id})
|
||||||
|
photo.update(**kwds)
|
||||||
|
return photo
|
||||||
|
|
||||||
|
def photo_view(self, photo_id, **kwds):
|
||||||
|
"""
|
||||||
|
Used to view the photo at a particular size.
|
||||||
|
Returns the requested photo object
|
||||||
|
"""
|
||||||
|
photo = Photo(self, {"id": photo_id})
|
||||||
|
photo.view(**kwds)
|
||||||
|
return photo
|
||||||
|
|
||||||
|
def photos_list(self, **kwds):
|
||||||
|
""" Returns a list of Photo objects """
|
||||||
|
photos = self.get("/photos/list.json", **kwds)["result"]
|
||||||
|
photos = self._result_to_list(photos)
|
||||||
|
return [Photo(self, photo) for photo in photos]
|
||||||
|
|
||||||
|
def photos_update(self, photo_ids, **kwds):
|
||||||
|
""" Updates a list of photos """
|
||||||
|
if not self._openphoto.post("/photos/update.json" % photo_ids,
|
||||||
|
**kwds)["result"]:
|
||||||
|
raise OpenPhotoError("Update response returned False")
|
||||||
|
|
||||||
|
def photos_delete(self, photo_ids, **kwds):
|
||||||
|
""" Deletes a list of photos """
|
||||||
|
if not self._openphoto.post("/photos/delete.json" % photo_ids,
|
||||||
|
**kwds)["result"]:
|
||||||
|
raise OpenPhotoError("Delete response returned False")
|
||||||
|
|
||||||
|
def photo_upload(self, photo_file, **kwds):
|
||||||
|
raise NotImplementedError("Use photo_upload_encoded instead.")
|
||||||
|
|
||||||
|
def photo_upload_encoded(self, photo_file, **kwds):
|
||||||
|
""" Base64-encodes and uploads the specified file """
|
||||||
|
encoded_photo = base64.b64encode(open(photo_file, "rb").read())
|
||||||
|
result = self.post("/photo/upload.json", photo=encoded_photo,
|
||||||
|
**kwds)["result"]
|
||||||
|
return Photo(self, result)
|
||||||
|
|
||||||
|
def photo_dynamic_url(self, photo_id, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def photo_next_previous(self, photo_id, **kwds):
|
||||||
|
"""
|
||||||
|
Returns a dict containing the next and previous photo objects,
|
||||||
|
given a photo in the middle.
|
||||||
|
"""
|
||||||
|
photo = Photo(self, {"id": photo_id})
|
||||||
|
return photo.next_previous(**kwds)
|
||||||
|
|
||||||
|
def photo_transform(self, photo_id, **kwds):
|
||||||
|
raise NotImplementedError()
|
25
openphoto/api_tag.py
Normal file
25
openphoto/api_tag.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from openphoto_http import OpenPhotoHttp, OpenPhotoError
|
||||||
|
from objects import Tag
|
||||||
|
|
||||||
|
class ApiTag(OpenPhotoHttp):
|
||||||
|
def tag_create(self, tag_id, **kwds):
|
||||||
|
""" Create a new tag and return it """
|
||||||
|
result = self.post("/tag/create.json", tag=tag_id, **kwds)["result"]
|
||||||
|
return Tag(self, result)
|
||||||
|
|
||||||
|
def tag_delete(self, tag_id, **kwds):
|
||||||
|
""" Delete a tag """
|
||||||
|
tag = Tag(self, {"id": tag_id})
|
||||||
|
tag.delete(**kwds)
|
||||||
|
|
||||||
|
def tag_update(self, tag_id, **kwds):
|
||||||
|
""" Update a tag """
|
||||||
|
tag = Tag(self, {"id": tag_id})
|
||||||
|
tag.update(**kwds)
|
||||||
|
return tag
|
||||||
|
|
||||||
|
def tags_list(self, **kwds):
|
||||||
|
""" Returns a list of Tag objects """
|
||||||
|
results = self.get("/tags/list.json", **kwds)["result"]
|
||||||
|
return [Tag(self, tag) for tag in results]
|
||||||
|
|
|
@ -6,9 +6,9 @@ import urllib
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import simplejson as json
|
|
||||||
except:
|
|
||||||
import json
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
from openphoto import OpenPhoto
|
from openphoto import OpenPhoto
|
||||||
|
|
||||||
|
@ -45,16 +45,16 @@ def main(args=sys.argv[1:]):
|
||||||
client = OpenPhoto(options.host, consumer_key, consumer_secret, token, token_secret)
|
client = OpenPhoto(options.host, consumer_key, consumer_secret, token, token_secret)
|
||||||
|
|
||||||
if options.method == "GET":
|
if options.method == "GET":
|
||||||
result = client.get(options.endpoint, params)
|
result = client.get_raw(options.endpoint, **params)
|
||||||
else:
|
else:
|
||||||
result = client.post(options.endpoint, params)
|
result = client.post_raw(options.endpoint, **params)
|
||||||
|
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
print "==========\nMethod: %s\nHost: %s\nEndpoint: %s" % (options.method, options.host, options.endpoint)
|
print "==========\nMethod: %s\nHost: %s\nEndpoint: %s" % (options.method, options.host, options.endpoint)
|
||||||
if len( params ) > 0:
|
if len( params ) > 0:
|
||||||
print "Fields:"
|
print "Fields:"
|
||||||
for kv in params.iteritems():
|
for kv in params.iteritems():
|
||||||
print " %s=%s" % kv
|
print " %s=%s" % kv
|
||||||
print "==========\n"
|
print "==========\n"
|
||||||
|
|
||||||
if options.pretty:
|
if options.pretty:
|
||||||
|
|
147
openphoto/objects.py
Normal file
147
openphoto/objects.py
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
from openphoto_http import OpenPhotoError, NotImplementedError
|
||||||
|
|
||||||
|
class OpenPhotoObject:
|
||||||
|
""" Base object supporting the storage of custom fields as attributes """
|
||||||
|
def __init__(self, openphoto, json_dict):
|
||||||
|
self._openphoto = openphoto
|
||||||
|
self._json_dict = json_dict
|
||||||
|
self._set_fields(json_dict)
|
||||||
|
|
||||||
|
def _set_fields(self, json_dict):
|
||||||
|
""" Set this object's attributes specified in json_dict """
|
||||||
|
for key, value in json_dict.items():
|
||||||
|
if key.startswith("_"):
|
||||||
|
raise ValueError("Illegal attribute: %s" % key)
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def _replace_fields(self, json_dict):
|
||||||
|
"""
|
||||||
|
Delete this object's attributes, and replace with
|
||||||
|
those in json_dict.
|
||||||
|
"""
|
||||||
|
for key in self._json_dict.keys():
|
||||||
|
delattr(self, key)
|
||||||
|
self._json_dict = json_dict
|
||||||
|
self._set_fields(json_dict)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if hasattr(self, "name"):
|
||||||
|
return "<%s name='%s'>" % (self.__class__, self.name)
|
||||||
|
elif hasattr(self, "id"):
|
||||||
|
return "<%s id='%s'>" % (self.__class__, self.id)
|
||||||
|
else:
|
||||||
|
return "<%s>" % (self.__class__)
|
||||||
|
|
||||||
|
def get_fields(self):
|
||||||
|
""" Returns this object's attributes """
|
||||||
|
return self._json_dict
|
||||||
|
|
||||||
|
|
||||||
|
class Photo(OpenPhotoObject):
|
||||||
|
def delete(self, **kwds):
|
||||||
|
""" Delete this photo """
|
||||||
|
self._openphoto.post("/photo/%s/delete.json" % self.id, **kwds)
|
||||||
|
self._replace_fields({})
|
||||||
|
|
||||||
|
def edit(self, **kwds):
|
||||||
|
""" Returns an HTML form to edit the photo """
|
||||||
|
result = self._openphoto.get("/photo/%s/edit.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
return result["markup"]
|
||||||
|
|
||||||
|
def replace(self, photo_file, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def replace_encoded(self, encoded_photo, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def update(self, **kwds):
|
||||||
|
""" Update this photo with the specified parameters """
|
||||||
|
new_dict = self._openphoto.post("/photo/%s/update.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
self._replace_fields(new_dict)
|
||||||
|
|
||||||
|
def view(self, **kwds):
|
||||||
|
"""
|
||||||
|
Used to view the photo at a particular size.
|
||||||
|
Updates the photo's fields with the response.
|
||||||
|
"""
|
||||||
|
new_dict = self._openphoto.get("/photo/%s/view.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
self._replace_fields(new_dict)
|
||||||
|
|
||||||
|
def dynamic_url(self, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def next_previous(self, **kwds):
|
||||||
|
""" Returns a dict containing the next and previous photo objects """
|
||||||
|
result = self._openphoto.get("/photo/%s/nextprevious.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
value = {}
|
||||||
|
if "next" in result:
|
||||||
|
value["next"] = Photo(self._openphoto, result["next"])
|
||||||
|
if "previous" in result:
|
||||||
|
value["previous"] = Photo(self._openphoto, result["previous"])
|
||||||
|
return value
|
||||||
|
|
||||||
|
def transform(self, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(OpenPhotoObject):
|
||||||
|
def delete(self, **kwds):
|
||||||
|
""" Delete this tag """
|
||||||
|
self._openphoto.post("/tag/%s/delete.json" % self.id, **kwds)
|
||||||
|
self._replace_fields({})
|
||||||
|
|
||||||
|
def update(self, **kwds):
|
||||||
|
""" Update this tag with the specified parameters """
|
||||||
|
new_dict = self._openphoto.post("/tag/%s/update.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
self._replace_fields(new_dict)
|
||||||
|
|
||||||
|
|
||||||
|
class Album(OpenPhotoObject):
|
||||||
|
def __init__(self, openphoto, json_dict):
|
||||||
|
OpenPhotoObject.__init__(self, openphoto, json_dict)
|
||||||
|
# Update the cover attribute with a photo object
|
||||||
|
if hasattr(self, "cover") and self.cover is not None:
|
||||||
|
self.cover = Photo(openphoto, self.cover)
|
||||||
|
|
||||||
|
def delete(self, **kwds):
|
||||||
|
""" Delete this album """
|
||||||
|
self._openphoto.post("/album/%s/delete.json" % self.id, **kwds)
|
||||||
|
self._replace_fields({})
|
||||||
|
|
||||||
|
def form(self, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def add_photos(self, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def remove_photos(self, **kwds):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def update(self, **kwds):
|
||||||
|
""" Update this album with the specified parameters """
|
||||||
|
self._openphoto.post("/album/%s/update.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
# Since the API doesn't give us the modified album, we need to
|
||||||
|
# update our fields based on the kwds that were sent
|
||||||
|
self._set_fields(kwds)
|
||||||
|
|
||||||
|
|
||||||
|
def view(self, **kwds):
|
||||||
|
"""
|
||||||
|
Requests the full contents of the album.
|
||||||
|
Updates the album's fields with the response.
|
||||||
|
"""
|
||||||
|
result = self._openphoto.get("/album/%s/view.json" % self.id,
|
||||||
|
**kwds)["result"]
|
||||||
|
# Update the cover attribute with a photo object
|
||||||
|
if result["cover"] is not None:
|
||||||
|
result["cover"] = Photo(self._openphoto, result["cover"])
|
||||||
|
# Update the photo list with photo objects
|
||||||
|
for i, photo in enumerate(result["photos"]):
|
||||||
|
result["photos"][i] = Photo(self._openphoto, result["photos"][i])
|
||||||
|
self._replace_fields(result)
|
135
openphoto/openphoto_http.py
Normal file
135
openphoto/openphoto_http.py
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import oauth2 as oauth
|
||||||
|
import urlparse
|
||||||
|
import urllib
|
||||||
|
import httplib2
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
except ImportError:
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
class OpenPhotoError(Exception):
|
||||||
|
""" Indicates that an OpenPhoto operation failed """
|
||||||
|
pass
|
||||||
|
|
||||||
|
class OpenPhotoDuplicateError(OpenPhotoError):
|
||||||
|
""" Indicates that an upload operation failed due to a duplicate photo """
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NotImplementedError(OpenPhotoError):
|
||||||
|
""" Indicates that the API function has not yet been coded - please help! """
|
||||||
|
pass
|
||||||
|
|
||||||
|
DUPLICATE_RESPONSE = {"code": 409,
|
||||||
|
"message": "This photo already exists"}
|
||||||
|
|
||||||
|
class OpenPhotoHttp:
|
||||||
|
""" Base class to handle HTTP requests to an OpenPhoto server """
|
||||||
|
def __init__(self, host, consumer_key='', consumer_secret='',
|
||||||
|
token='', token_secret=''):
|
||||||
|
self._host = host
|
||||||
|
self._consumer_key = consumer_key
|
||||||
|
self._consumer_secret = consumer_secret
|
||||||
|
self._token = token
|
||||||
|
self._token_secret = token_secret
|
||||||
|
|
||||||
|
def get(self, endpoint, **params):
|
||||||
|
"""
|
||||||
|
Performs an HTTP GET from the specified endpoint (API path),
|
||||||
|
passing parameters if given.
|
||||||
|
Returns the decoded JSON dictionary, and
|
||||||
|
raises exceptions if an error code is received.
|
||||||
|
"""
|
||||||
|
response = json.loads(self.get_raw(endpoint, **params))
|
||||||
|
self._process_response(response)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def post(self, endpoint, **params):
|
||||||
|
"""
|
||||||
|
Performs an HTTP POST to the specified endpoint (API path),
|
||||||
|
passing parameters if given.
|
||||||
|
Returns the decoded JSON dictionary, and
|
||||||
|
raises exceptions if an error code is received.
|
||||||
|
"""
|
||||||
|
response = json.loads(self.post_raw(endpoint, **params))
|
||||||
|
self._process_response(response)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get_raw(self, endpoint, **params):
|
||||||
|
"""
|
||||||
|
Performs an HTTP GET from the specified endpoint (API path),
|
||||||
|
passing parameters if given.
|
||||||
|
Returns the raw HTTP content string.
|
||||||
|
"""
|
||||||
|
params = self._process_params(params)
|
||||||
|
url = urlparse.urlunparse(('http', self._host, endpoint, '',
|
||||||
|
urllib.urlencode(params), ''))
|
||||||
|
if self._consumer_key:
|
||||||
|
consumer = oauth.Consumer(self._consumer_key, self._consumer_secret)
|
||||||
|
token = oauth.Token(self._token, self._token_secret)
|
||||||
|
client = oauth.Client(consumer, token)
|
||||||
|
else:
|
||||||
|
client = httplib2.Http()
|
||||||
|
|
||||||
|
_, content = client.request(url, "GET")
|
||||||
|
return content
|
||||||
|
|
||||||
|
def post_raw(self, endpoint, **params):
|
||||||
|
"""
|
||||||
|
Performs an HTTP POST to the specified endpoint (API path),
|
||||||
|
passing parameters if given.
|
||||||
|
Returns the raw HTTP content string.
|
||||||
|
"""
|
||||||
|
params = self._process_params(params)
|
||||||
|
url = urlparse.urlunparse(('http', self._host, endpoint, '', '', ''))
|
||||||
|
|
||||||
|
if not self._consumer_key:
|
||||||
|
raise OpenPhotoError("Cannot issue POST without OAuth tokens")
|
||||||
|
|
||||||
|
consumer = oauth.Consumer(self._consumer_key, self._consumer_secret)
|
||||||
|
token = oauth.Token(self._token, self._token_secret)
|
||||||
|
|
||||||
|
client = oauth.Client(consumer, token)
|
||||||
|
body = urllib.urlencode(params)
|
||||||
|
_, content = client.request(url, "POST", body)
|
||||||
|
return content
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _process_params(params):
|
||||||
|
""" Converts Unicode/lists/booleans inside HTTP parameters """
|
||||||
|
processed_params = {}
|
||||||
|
for key, value in params.items():
|
||||||
|
# Use UTF-8 encoding
|
||||||
|
if isinstance(value, unicode):
|
||||||
|
value = value.encode('utf-8')
|
||||||
|
# Handle lists
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = ",".join(value)
|
||||||
|
# Handle booleans
|
||||||
|
if isinstance(value, bool):
|
||||||
|
value = 1 if value else 0
|
||||||
|
processed_params[key] = value
|
||||||
|
return processed_params
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _process_response(response):
|
||||||
|
""" Raises an exception if an invalid response code is received """
|
||||||
|
if response["code"] >= 200 and response["code"] < 300:
|
||||||
|
return
|
||||||
|
|
||||||
|
error_message = "Code %d: %s" % (response["code"],
|
||||||
|
response["message"])
|
||||||
|
|
||||||
|
# Special case for a duplicate photo error
|
||||||
|
if (response["code"] == DUPLICATE_RESPONSE["code"] and
|
||||||
|
DUPLICATE_RESPONSE["message"] in response["message"]):
|
||||||
|
raise OpenPhotoDuplicateError(error_message)
|
||||||
|
|
||||||
|
raise OpenPhotoError(error_message)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _result_to_list(result):
|
||||||
|
""" Handle the case where the result contains no items """
|
||||||
|
if result[0]["totalRows"] == 0:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return result
|
4
setup.py
Normal file → Executable file
4
setup.py
Normal file → Executable file
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
requires = ['oauth2', 'httplib2']
|
requires = ['oauth2', 'httplib2']
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
|
@ -17,7 +19,7 @@ except ImportError:
|
||||||
'requires': requires}
|
'requires': requires}
|
||||||
|
|
||||||
setup(name='openphoto',
|
setup(name='openphoto',
|
||||||
version='0.1',
|
version='0.2',
|
||||||
description='Client library for the openphoto project',
|
description='Client library for the openphoto project',
|
||||||
author='James Walker',
|
author='James Walker',
|
||||||
author_email='walkah@walkah.net',
|
author_email='walkah@walkah.net',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue