Some major refactoring inside binder.py.
authorJoshua <jroesslein@gmail.com>
Sat, 30 Jan 2010 18:46:26 +0000 (12:46 -0600)
committerJoshua <jroesslein@gmail.com>
Sat, 30 Jan 2010 18:46:26 +0000 (12:46 -0600)
tweepy/binder.py

index 0a80ca220006ea0ddd3efbcec52066e9337e02b6..1d0ba66d2b6d0963a2e056b583efebce968ac031 100644 (file)
@@ -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