Commit c48d352a authored by Joshua's avatar Joshua
Browse files

Some major refactoring inside binder.py.

parent 52d95381
......@@ -12,157 +12,178 @@ from tweepy.error import TweepError
re_path_template = re.compile('{\w+}')
def bind_api(path, payload_type=None, payload_list=False, allowed_param=[], method='GET',
require_auth=False, timeout=None, search_api = False):
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)
timeout = config.get('timeout', None)
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 TweepError('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()
def _call(api, *args, **kargs):
# If require auth, throw exception if credentials not provided
if require_auth and not api.auth:
raise TweepError('Authentication required!')
# check for post data
post_data = kargs.pop('post_data', None)
# check for retry request parameters
retry_count = kargs.pop('retry_count', api.retry_count)
retry_delay = kargs.pop('retry_delay', api.retry_delay)
retry_errors = kargs.pop('retry_errors', api.retry_errors)
# check for headers
headers = kargs.pop('headers', {})
# build parameter dict
parameters = {}
for idx, arg in enumerate(args):
if isinstance(arg, unicode):
arg = arg.encode('utf-8')
elif not isinstance(arg, str):
arg = str(arg)
try:
parameters[allowed_param[idx]] = arg
except IndexError:
raise TweepError('Too many parameters supplied!')
for k, arg in kargs.items():
if arg is None:
continue
if k in parameters:
raise TweepError('Multiple values for parameter %s supplied!' % k)
if isinstance(arg, unicode):
arg = arg.encode('utf-8')
elif not isinstance(arg, str):
arg = str(arg)
parameters[k] = arg
# Pick correct URL root to use
if search_api is False:
api_root = api.api_root
else:
api_root = api.search_root
# Build the request URL
if len(parameters):
# Replace any template variables in path
tpath = str(path)
for template in re_path_template.findall(tpath):
name = template.strip('{}')
try:
value = urllib.quote(parameters[name])
tpath = tpath.replace(template, value)
except KeyError:
raise TweepError('Invalid path key: %s' % name)
del parameters[name]
url = '%s?%s' % (api_root + tpath, urllib.urlencode(parameters))
else:
url = api_root + path
# Check cache if caching enabled and method is GET
if api.cache and method == 'GET':
cache_result = api.cache.get(url, timeout)
# 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 = api
else:
cache_result._api = api
return cache_result
# get scheme and host
if api.secure:
scheme = 'https://'
else:
scheme = 'http://'
if search_api is False:
host = api.host
else:
host = api.search_host
# Continue attempting request until successful
# or maximum number of retries is reached.
retries_performed = 0
while retries_performed < retry_count + 1:
# Open connection
# FIXME: add timeout
if api.secure:
conn = httplib.HTTPSConnection(host)
self.scheme = 'https://'
else:
conn = httplib.HTTPConnection(host)
# Apply authentication
if api.auth:
api.auth.apply_auth(
scheme + host + url,
method, headers, parameters
)
# Build request
try:
conn.request(method, url, headers=headers, body=post_data)
except Exception, e:
raise TweepError('Failed to send request: %s' % e)
# Get response
resp = conn.getresponse()
# Exit request loop if non-retry error code
if retry_errors is None:
if resp.status == 200: break
self.scheme = 'http://'
if self.search_api:
self.host = api.search_host
else:
if resp.status not in retry_errors: break
self.host = api.host
def build_parameters(self, args, kargs):
self.parameters = {}
for idx, arg in enumerate(args):
if isinstance(arg, unicode):
arg = arg.encode('utf-8')
elif not isinstance(arg, str):
arg = str(arg)
try:
self.parameters[self.allowed_param[idx]] = arg
except IndexError:
raise TweepError('Too many parameters supplied!')
for k, arg in kargs.items():
if arg is None:
continue
if k in self.parameters:
raise TweepError('Multiple values for parameter %s supplied!' % k)
if isinstance(arg, unicode):
arg = arg.encode('utf-8')
elif not isinstance(arg, str):
arg = str(arg)
self.parameters[k] = 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])
self.path = self.path.replace(variable, value)
except KeyError:
raise TweepError('No parameter value found for path variable: %s' % name)
del self.parameters[name]
def execute(self):
# Build the request URL
url = self.api_root + self.path
if len(self.parameters):
url = '%s?%s' % (url, 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, self.timeout)
# 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
# Continue attempting request until successful
# or maximum number of retries is reached.
retries_performed = 0
while retries_performed < self.retry_count + 1:
# Open connection
# FIXME: add timeout
if self.api.secure:
conn = httplib.HTTPSConnection(self.host)
else:
conn = httplib.HTTPConnection(self.host)
# Apply authentication
if self.api.auth:
self.api.auth.apply_auth(
self.scheme + self.host + url,
self.method, self.headers, self.parameters
)
# Execute request
try:
conn.request(self.method, url, headers=self.headers, body=self.post_data)
resp = conn.getresponse()
except Exception, e:
raise TweepError('Failed to send request: %s' % e)
# Exit request loop if non-retry error code
if self.retry_errors:
if resp.status not in self.retry_errors: break
else:
if resp.status == 200: break
# Sleep before retrying request again
time.sleep(retry_delay)
retries_performed += 1
# Sleep before retrying request again
time.sleep(self.retry_delay)
retries_performed += 1
# If an error was returned, throw an exception
api.last_response = resp
if resp.status != 200:
try:
error_msg = parse_error(json.loads(resp.read()))
except Exception:
error_msg = "Twitter error response: status code = %s" % resp.status
raise TweepError(error_msg)
# If an error was returned, throw an exception
self.api.last_response = resp
if resp.status != 200:
try:
#TODO: parse error message
raise Exception
except Exception:
error_msg = "Twitter error response: status code = %s" % resp.status
raise TweepError(error_msg)
# Parse the response payload
result = self.api.parser.parse(self.api, self.payload_type, self.payload_list, resp.read())
conn.close()
# Parse the response payload
result = api.parser.parse(api, payload_type, payload_list, resp.read())
# Store result into cache if one is available.
if self.api.cache and self.method == 'GET' and result:
self.api.cache.store(url, result)
conn.close()
return result
# store result in cache
if api.cache and method == 'GET' and result:
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 allowed_param:
if 'cursor' in APIMethod.allowed_param:
_call.pagination_mode = 'cursor'
elif 'page' in allowed_param:
elif 'page' in APIMethod.allowed_param:
_call.pagination_mode = 'page'
return _call
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment