From: Joshua Date: Sat, 30 Jan 2010 18:46:26 +0000 (-0600) Subject: Some major refactoring inside binder.py. X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=c48d352ad5890c4e57afb8a347630eb5dd1cdf77;p=tweepy.git Some major refactoring inside binder.py. --- diff --git a/tweepy/binder.py b/tweepy/binder.py index 0a80ca2..1d0ba66 100644 --- a/tweepy/binder.py +++ b/tweepy/binder.py @@ -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