Updated to use requests and requests_oauthlib
To allow Python3 support. Also removes quite a bit of multipart post magic.
This commit is contained in:
parent
792632fdf4
commit
326394fed9
5 changed files with 46 additions and 78 deletions
|
@ -80,7 +80,9 @@ class ApiPhoto:
|
||||||
return photo
|
return photo
|
||||||
|
|
||||||
def upload(self, photo_file, **kwds):
|
def upload(self, photo_file, **kwds):
|
||||||
result = self._client.post("/photo/upload.json", files={'photo': photo_file},
|
result = self._client.post("/photo/upload.json",
|
||||||
|
files={'photo': (photo_file,
|
||||||
|
open(photo_file, 'rb'))},
|
||||||
**kwds)["result"]
|
**kwds)["result"]
|
||||||
return Photo(self._client, result)
|
return Photo(self._client, result)
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,8 @@ def extract_files(params):
|
||||||
updated_params = {}
|
updated_params = {}
|
||||||
for name in params:
|
for name in params:
|
||||||
if name == "photo" and params[name].startswith("@") and os.path.isfile(os.path.expanduser(params[name][1:])):
|
if name == "photo" and params[name].startswith("@") and os.path.isfile(os.path.expanduser(params[name][1:])):
|
||||||
files[name] = params[name][1:]
|
filename = params[name][1:]
|
||||||
|
files[name] = (filename, open(filename, 'rb'))
|
||||||
else:
|
else:
|
||||||
updated_params[name] = params[name]
|
updated_params[name] = params[name]
|
||||||
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import os
|
|
||||||
import mimetypes
|
|
||||||
import mimetools
|
|
||||||
|
|
||||||
def encode_multipart_formdata(params, files):
|
|
||||||
boundary = mimetools.choose_boundary()
|
|
||||||
|
|
||||||
lines = []
|
|
||||||
for name in params:
|
|
||||||
lines.append("--" + boundary)
|
|
||||||
lines.append("Content-Disposition: form-data; name=\"%s\"" % name)
|
|
||||||
lines.append("")
|
|
||||||
lines.append(str(params[name]))
|
|
||||||
for name in files:
|
|
||||||
filename = files[name]
|
|
||||||
content_type, _ = mimetypes.guess_type(filename)
|
|
||||||
if content_type is None:
|
|
||||||
content_type = "application/octet-stream"
|
|
||||||
|
|
||||||
lines.append("--" + boundary)
|
|
||||||
lines.append("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"" % (name, filename))
|
|
||||||
lines.append("Content-Type: %s" % content_type)
|
|
||||||
lines.append("")
|
|
||||||
lines.append(open(os.path.expanduser(filename), "rb").read())
|
|
||||||
lines.append("--" + boundary + "--")
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
body = "\r\n".join(lines)
|
|
||||||
headers = {'Content-Type': "multipart/form-data; boundary=%s" % boundary,
|
|
||||||
'Content-Length': str(len(body))}
|
|
||||||
return headers, body
|
|
|
@ -1,8 +1,8 @@
|
||||||
import os
|
import os
|
||||||
import oauth2 as oauth
|
|
||||||
import urlparse
|
import urlparse
|
||||||
import urllib
|
import urllib
|
||||||
import httplib2
|
import requests
|
||||||
|
import requests_oauthlib
|
||||||
import logging
|
import logging
|
||||||
import StringIO
|
import StringIO
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
@ -13,7 +13,6 @@ except ImportError:
|
||||||
|
|
||||||
from objects import OpenPhotoObject
|
from objects import OpenPhotoObject
|
||||||
from errors import *
|
from errors import *
|
||||||
from multipart_post import encode_multipart_formdata
|
|
||||||
|
|
||||||
DUPLICATE_RESPONSE = {"code": 409,
|
DUPLICATE_RESPONSE = {"code": 409,
|
||||||
"message": "This photo already exists"}
|
"message": "This photo already exists"}
|
||||||
|
@ -78,28 +77,28 @@ class OpenPhotoHttp:
|
||||||
endpoint = "/v%d%s" % (self._api_version, endpoint)
|
endpoint = "/v%d%s" % (self._api_version, endpoint)
|
||||||
url = urlparse.urlunparse(('http', self._host, endpoint, '',
|
url = urlparse.urlunparse(('http', self._host, endpoint, '',
|
||||||
urllib.urlencode(params), ''))
|
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()
|
|
||||||
|
|
||||||
response, content = client.request(url, "GET")
|
if self._consumer_key:
|
||||||
|
auth = requests_oauthlib.OAuth1(self._consumer_key, self._consumer_secret,
|
||||||
|
self._token, self._token_secret)
|
||||||
|
else:
|
||||||
|
auth = None
|
||||||
|
|
||||||
|
response = requests.get(url, auth=auth)
|
||||||
|
|
||||||
self._logger.info("============================")
|
self._logger.info("============================")
|
||||||
self._logger.info("GET %s" % url)
|
self._logger.info("GET %s" % url)
|
||||||
self._logger.info("---")
|
self._logger.info("---")
|
||||||
self._logger.info(content)
|
self._logger.info(response.text)
|
||||||
|
|
||||||
self.last_url = url
|
self.last_url = url
|
||||||
self.last_params = params
|
self.last_params = params
|
||||||
self.last_response = (response, content)
|
self.last_response = response
|
||||||
|
|
||||||
if process_response:
|
if process_response:
|
||||||
return self._process_response(response, content)
|
return self._process_response(response)
|
||||||
else:
|
else:
|
||||||
return content
|
return response.text
|
||||||
|
|
||||||
def post(self, endpoint, process_response=True, files = {}, **params):
|
def post(self, endpoint, process_response=True, files = {}, **params):
|
||||||
"""
|
"""
|
||||||
|
@ -122,19 +121,18 @@ class OpenPhotoHttp:
|
||||||
if not self._consumer_key:
|
if not self._consumer_key:
|
||||||
raise OpenPhotoError("Cannot issue POST without OAuth tokens")
|
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)
|
|
||||||
|
|
||||||
if files:
|
if files:
|
||||||
# Parameters must be signed and encoded into the multipart body
|
# Parameters must be signed before being encoded into the multipart body
|
||||||
signed_params = self._sign_params(client, url, params)
|
# Can't use the standard requests methods to do this, since
|
||||||
headers, body = encode_multipart_formdata(signed_params, files)
|
# we need to keep the multipart data from being signed.
|
||||||
|
headers = self._sign_params(url, params)
|
||||||
|
auth = None
|
||||||
else:
|
else:
|
||||||
body = urllib.urlencode(params)
|
|
||||||
headers = None
|
headers = None
|
||||||
|
auth = requests_oauthlib.OAuth1(self._consumer_key, self._consumer_secret,
|
||||||
|
self._token, self._token_secret)
|
||||||
|
|
||||||
response, content = client.request(url, "POST", body, headers)
|
response = requests.post(url, data=params, headers=headers, files=files, auth=auth)
|
||||||
|
|
||||||
self._logger.info("============================")
|
self._logger.info("============================")
|
||||||
self._logger.info("POST %s" % url)
|
self._logger.info("POST %s" % url)
|
||||||
|
@ -142,27 +140,25 @@ class OpenPhotoHttp:
|
||||||
if files:
|
if files:
|
||||||
self._logger.info("files: %s" % repr(files))
|
self._logger.info("files: %s" % repr(files))
|
||||||
self._logger.info("---")
|
self._logger.info("---")
|
||||||
self._logger.info(content)
|
self._logger.info(response.text)
|
||||||
|
|
||||||
self.last_url = url
|
self.last_url = url
|
||||||
self.last_params = params
|
self.last_params = params
|
||||||
self.last_response = (response, content)
|
self.last_response = response
|
||||||
|
|
||||||
if process_response:
|
if process_response:
|
||||||
return self._process_response(response, content)
|
return self._process_response(response)
|
||||||
else:
|
else:
|
||||||
return content
|
return response.text
|
||||||
|
|
||||||
@staticmethod
|
def _sign_params(self, url, params):
|
||||||
def _sign_params(client, url, params):
|
"""Use OAuth to sign a dictionary of params, returning the authorization header"""
|
||||||
"""Use OAuth to sign a dictionary of params"""
|
oauth = requests_oauthlib.core.Client(self._consumer_key, self._consumer_secret,
|
||||||
request = oauth.Request.from_consumer_and_token(consumer=client.consumer,
|
self._token, self._token_secret)
|
||||||
token=client.token,
|
url, headers, data = oauth.sign(url, http_method="POST", body=params,
|
||||||
http_method="POST",
|
headers={'Content-Type': requests_oauthlib.core.CONTENT_TYPE_FORM_URLENCODED})
|
||||||
http_url=url,
|
del(headers['Content-Type'])
|
||||||
parameters=params)
|
return headers
|
||||||
request.sign_request(client.method, client.consumer, client.token)
|
|
||||||
return dict(urlparse.parse_qsl(request.to_postdata()))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _process_params(params):
|
def _process_params(params):
|
||||||
|
@ -196,24 +192,24 @@ class OpenPhotoHttp:
|
||||||
return processed_params
|
return processed_params
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _process_response(response, content):
|
def _process_response(response):
|
||||||
"""
|
"""
|
||||||
Decodes the JSON response, returning a dict.
|
Decodes the JSON response, returning a dict.
|
||||||
Raises an exception if an invalid response code is received.
|
Raises an exception if an invalid response code is received.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
json_response = json.loads(content)
|
json_response = response.json()
|
||||||
code = json_response["code"]
|
code = json_response["code"]
|
||||||
message = json_response["message"]
|
message = json_response["message"]
|
||||||
except ValueError, KeyError:
|
except ValueError, KeyError:
|
||||||
# Response wasn't OpenPhoto JSON - check the HTTP status code
|
# Response wasn't OpenPhoto JSON - check the HTTP status code
|
||||||
if 200 <= response.status < 300:
|
if 200 <= response.status_code < 300:
|
||||||
# Status code was valid, so just reraise the exception
|
# Status code was valid, so just reraise the exception
|
||||||
raise
|
raise
|
||||||
elif response.status == 404:
|
elif response.status_code == 404:
|
||||||
raise OpenPhoto404Error("HTTP Error %d: %s" % (response.status, response.reason))
|
raise OpenPhoto404Error("HTTP Error %d: %s" % (response.status_code, response.reason))
|
||||||
else:
|
else:
|
||||||
raise OpenPhotoError("HTTP Error %d: %s" % (response.status, response.reason))
|
raise OpenPhotoError("HTTP Error %d: %s" % (response.status_code, response.reason))
|
||||||
|
|
||||||
if 200 <= code < 300:
|
if 200 <= code < 300:
|
||||||
return json_response
|
return json_response
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
requires = ['oauth2', 'httplib2']
|
requires = ['requests', 'requests-oauthlib']
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue