Skip to content

Instantly share code, notes, and snippets.

@nkchenz
Created November 11, 2011 10:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nkchenz/1357670 to your computer and use it in GitHub Desktop.
Save nkchenz/1357670 to your computer and use it in GitHub Desktop.
weibopy-binder-urllib2
# Copyright 2009-2010 Joshua Roesslein
# See LICENSE for details.
import httplib
import urllib
import time
import re
from weibopy.error import WeibopError
from weibopy.utils import convert_to_utf8_str
import urllib2
re_path_template = re.compile('{\w+}')
def bind_api(**config):
class APIMethod(object):
path = config['path']
payload_type = config.get('payload_type', None)
payload_list = config.get('payload_list', False)
allowed_param = config.get('allowed_param', [])
method = config.get('method', 'GET')
require_auth = config.get('require_auth', False)
search_api = config.get('search_api', False)
def __init__(self, api, args, kargs):
# If authentication is required and no credentials
# are provided, throw an error.
if self.require_auth and not api.auth:
raise WeibopError('Authentication required!')
self.api = api
self.post_data = kargs.pop('post_data', None)
self.retry_count = kargs.pop('retry_count', api.retry_count)
self.retry_delay = kargs.pop('retry_delay', api.retry_delay)
self.retry_errors = kargs.pop('retry_errors', api.retry_errors)
self.headers = kargs.pop('headers', {})
self.build_parameters(args, kargs)
# Pick correct URL root to use
if self.search_api:
self.api_root = api.search_root
else:
self.api_root = api.api_root
# Perform any path variable substitution
self.build_path()
if api.secure:
self.scheme = 'https://'
else:
self.scheme = 'http://'
if self.search_api:
self.host = api.search_host
else:
self.host = api.host
# Manually set Host header to fix an issue in python 2.5
# or older where Host is set including the 443 port.
# This causes Twitter to issue 301 redirect.
# See Issue http://github.com/joshthecoder/tweepy/issues/#issue/12
self.headers['Host'] = self.host
def build_parameters(self, args, kargs):
self.parameters = {}
for idx, arg in enumerate(args):
try:
self.parameters[self.allowed_param[idx]] = convert_to_utf8_str(arg)
except IndexError:
raise WeibopError('Too many parameters supplied!')
for k, arg in kargs.items():
if arg is None:
continue
if k in self.parameters:
raise WeibopError('Multiple values for parameter %s supplied!' % k)
self.parameters[k] = convert_to_utf8_str(arg)
def build_path(self):
for variable in re_path_template.findall(self.path):
name = variable.strip('{}')
if name == 'user' and self.api.auth:
value = self.api.auth.get_username()
else:
try:
value = urllib.quote(self.parameters[name])
except KeyError:
raise WeibopError('No parameter value found for path variable: %s' % name)
del self.parameters[name]
self.path = self.path.replace(variable, value)
def execute(self):
# Build the request URL
url = self.api_root + self.path
if self.api.source is not None:
self.parameters.setdefault('source',self.api.source)
if len(self.parameters):
if self.method == 'GET' or self.method == 'DELETE':
url = '%s?%s' % (url, urllib.urlencode(self.parameters))
else:
self.headers.setdefault("User-Agent","python")
if self.post_data is None:
self.headers.setdefault("Accept","text/html")
self.headers.setdefault("Content-Type","application/x-www-form-urlencoded")
self.post_data = urllib.urlencode(self.parameters)
# Query the cache if one is available
# and this request uses a GET method.
if self.api.cache and self.method == 'GET':
cache_result = self.api.cache.get(url)
# if cache result found and not expired, return it
if cache_result:
# must restore api reference
if isinstance(cache_result, list):
for result in cache_result:
result._api = self.api
else:
cache_result._api = self.api
return cache_result
#urllib.urlencode(self.parameters)
# Continue attempting request until successful
# or maximum number of retries is reached.
sTime = time.time()
retries_performed = 0
while retries_performed < self.retry_count + 1:
# Apply authentication
if self.api.auth:
self.api.auth.apply_auth(
self.scheme + self.host + url,
self.method, self.headers, self.parameters
)
# Execute request
resp =None
action_url = self.scheme + self.host + url
req = urllib2.Request(action_url, self.post_data, self.headers)
req.method = self.method
# FIXME: When to retry and when to raise?
try:
resp = urllib2.urlopen(req)
except urllib2.URLError, e:
raise WeibopError('Failed to send request: %s, url %s, headers %s' % (e, action_url, self.headers))
# If no exception and status is OK
if resp and resp.code == 200:
break
# Something wrong
if self.retry_errors:
if resp.code not in self.retry_errors:
break
# Sleep before retrying request again
time.sleep(self.retry_delay)
retries_performed += 1
# If an error was returned, throw an exception
body = ''
if resp:
body = resp.read()
self.api.last_response = resp
if self.api.log is not None:
requestUrl = "URL:http://"+ self.host + url
eTime = '%.0f' % ((time.time() - sTime) * 1000)
postData = ""
if self.post_data is not None:
postData = ",post:"+ self.post_data[0:500]
self.api.log.debug(requestUrl +",time:"+ str(eTime)+ postData+",result:"+ body )
# FIXME: What about resp is None?
if resp and resp.code != 200:
try:
json = self.api.parser.parse_error(self, body)
error_code = json['error_code']
error = json['error']
error_msg = 'error_code:' + error_code +','+ error
except Exception:
error_msg = "Weibo error response: status code = %s" % resp.code
raise WeibopError(error_msg)
# Parse the response payload
result = self.api.parser.parse(self, body)
# Store result into cache if one is available.
if self.api.cache and self.method == 'GET' and result:
self.api.cache.store(url, result)
return result
def _call(api, *args, **kargs):
method = APIMethod(api, args, kargs)
return method.execute()
# Set pagination mode
if 'cursor' in APIMethod.allowed_param:
_call.pagination_mode = 'cursor'
elif 'page' in APIMethod.allowed_param:
_call.pagination_mode = 'page'
return _call
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment