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