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:
sneakypete81 2013-05-12 17:25:50 +01:00
parent 792632fdf4
commit 326394fed9
5 changed files with 46 additions and 78 deletions

View file

@ -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)

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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: