From ad81c73923a8093616cd74995cc6f2af7920136d Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Fri, 24 Feb 2012 12:27:50 -0500 Subject: [PATCH 1/4] More verbose error names I feel like the names should be a little more verbose than just "APILimit" or "RateLimitError"... just added "Twython" before the class name. --- twython/twython.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/twython/twython.py b/twython/twython.py index 801792a..34c1fdb 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -72,23 +72,23 @@ else: class TwythonError(AttributeError): """ Generic error class, catch-all for most Twython issues. - Special cases are handled by APILimit and AuthError. + Special cases are handled by TwythonAPILimit and TwythonAuthError. Note: To use these, the syntax has changed as of Twython 1.3. To catch these, you need to explicitly import them into your code, e.g: - from twython import TwythonError, APILimit, AuthError + from twython import TwythonError, TwythonAPILimit, TwythonAuthError """ def __init__(self, msg, error_code=None): self.msg = msg if error_code == 400: - raise APILimit(msg) + raise TwythonAPILimit(msg) def __str__(self): return repr(self.msg) -class APILimit(TwythonError): +class TwythonAPILimit(TwythonError): """ Raised when you've hit an API limit. Try to avoid these, read the API docs if you're running into issues here, Twython does not concern itself with @@ -100,7 +100,7 @@ class APILimit(TwythonError): def __str__(self): return repr(self.msg) -class RateLimitError(TwythonError): +class TwythonRateLimitError(TwythonError): """ Raised when you've hit a rate limit. retry_wait_seconds is the number of seconds to wait before trying again. @@ -113,7 +113,7 @@ class RateLimitError(TwythonError): return repr(self.msg) -class AuthError(TwythonError): +class TwythonAuthError(TwythonError): """ Raised when you try to access a protected resource and it fails due to some issue with your authentication. @@ -219,7 +219,7 @@ class Twython(object): resp, content = self.client.request(self.request_token_url, "GET", **request_args) if resp['status'] != '200': - raise AuthError("Seems something couldn't be verified with your OAuth junk. Error: %s, Message: %s" % (resp['status'], content)) + raise TwythonAuthError("Seems something couldn't be verified with your OAuth junk. Error: %s, Message: %s" % (resp['status'], content)) request_tokens = dict(parse_qsl(content)) @@ -313,7 +313,7 @@ class Twython(object): if int(resp.status) == 420: retry_wait_seconds = resp['retry-after'] - raise RateLimitError("getSearchTimeline() is being rate limited. Retry after %s seconds." % + raise TwythonRateLimitError("getSearchTimeline() is being rate limited. Retry after %s seconds." % retry_wait_seconds, retry_wait_seconds, resp.status) From 95f7abc7a35526333db5083d21bce0742d03bf28 Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Fri, 24 Feb 2012 12:36:22 -0500 Subject: [PATCH 2/4] PEP8 Cleanup Attempting to clean up some code. * There were a lot of libraries not being used any longer * Changed some vars named "id" to "id_", id is a reserved var --- twython/twython.py | 115 +++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/twython/twython.py b/twython/twython.py index 34c1fdb..2088f3b 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -11,14 +11,9 @@ __author__ = "Ryan McGrath " __version__ = "1.4.6" -import cgi import urllib import urllib2 -import urlparse -import httplib import httplib2 -import mimetypes -import mimetools import re import inspect import time @@ -62,13 +57,14 @@ OAUTH_LIB_SUPPORTS_CALLBACK = False if not hasattr(oauth, '_version') or float(oauth._version.manual_verstr) <= 1.4: OAUTH_CLIENT_INSPECTION = inspect.getargspec(oauth.Client.request) try: - OAUTH_LIB_SUPPORTS_CALLBACK = 'callback_url' in OAUTH_CLIENT_INSPECTION.args + OAUTH_LIB_SUPPORTS_CALLBACK = 'callback_url' in OAUTH_CLIENT_INSPECTION.args except AttributeError: # Python 2.5 doesn't return named tuples, so don't look for an args section specifically. OAUTH_LIB_SUPPORTS_CALLBACK = 'callback_url' in OAUTH_CLIENT_INSPECTION else: OAUTH_CALLBACK_IN_URL = True + class TwythonError(AttributeError): """ Generic error class, catch-all for most Twython issues. @@ -100,6 +96,7 @@ class TwythonAPILimit(TwythonError): def __str__(self): return repr(self.msg) + class TwythonRateLimitError(TwythonError): """ Raised when you've hit a rate limit. retry_wait_seconds is the number of seconds to @@ -126,7 +123,8 @@ class TwythonAuthError(TwythonError): class Twython(object): - def __init__(self, twitter_token = None, twitter_secret = None, oauth_token = None, oauth_token_secret = None, headers=None, callback_url=None, client_args={}): + def __init__(self, twitter_token=None, twitter_secret=None, oauth_token=None, oauth_token_secret=None, \ + headers=None, callback_url=None, client_args=None): """setup(self, oauth_token = None, headers = None) Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below). @@ -140,7 +138,8 @@ class Twython(object): headers - User agent header, dictionary style ala {'User-Agent': 'Bert'} client_args - additional arguments for HTTP client (see httplib2.Http.__init__), e.g. {'timeout': 10.0} - ** Note: versioning is not currently used by search.twitter functions; when Twitter moves their junk, it'll be supported. + ** Note: versioning is not currently used by search.twitter functions; + when Twitter moves their junk, it'll be supported. """ # Needed for hitting that there API. self.request_token_url = 'http://twitter.com/oauth/request_token' @@ -161,6 +160,8 @@ class Twython(object): self.consumer = None self.token = None + client_args = client_args or {} + if self.twitter_token is not None and self.twitter_secret is not None: self.consumer = oauth.Consumer(self.twitter_token, self.twitter_secret) @@ -176,6 +177,7 @@ class Twython(object): # If they don't do authentication, but still want to request unprotected resources, we need an opener. self.client = httplib2.Http(**client_args) # register available funcs to allow listing name when debugging. + def setFunc(key): return lambda **kwargs: self._constructFunc(key, **kwargs) for key in api_table.keys(): @@ -196,10 +198,10 @@ class Twython(object): # and junk, handle errors/authentication if fn['method'] == 'POST': myargs = urllib.urlencode(dict([k, Twython.encode(v)] for k, v in kwargs.items())) - resp, content = self.client.request(base, fn['method'], myargs, headers = self.headers) + resp, content = self.client.request(base, fn['method'], myargs, headers=self.headers) else: - myargs = ["%s=%s" %(key, value) for (key, value) in kwargs.iteritems()] - url = "%s?%s" %(base, "&".join(myargs)) + myargs = ["%s=%s" % (key, value) for (key, value) in kwargs.iteritems()] + url = "%s?%s" % (base, "&".join(myargs)) resp, content = self.client.request(url, fn['method'], headers=self.headers) return simplejson.loads(content.decode('utf-8')) @@ -211,35 +213,35 @@ class Twython(object): Returns an authorization URL for a user to hit. """ callback_url = self.callback_url or 'oob' - + request_args = {} if OAUTH_LIB_SUPPORTS_CALLBACK: request_args['callback_url'] = callback_url - + resp, content = self.client.request(self.request_token_url, "GET", **request_args) if resp['status'] != '200': raise TwythonAuthError("Seems something couldn't be verified with your OAuth junk. Error: %s, Message: %s" % (resp['status'], content)) - + request_tokens = dict(parse_qsl(content)) - - oauth_callback_confirmed = request_tokens.get('oauth_callback_confirmed')=='true' - + + oauth_callback_confirmed = request_tokens.get('oauth_callback_confirmed') == 'true' + if not OAUTH_LIB_SUPPORTS_CALLBACK and callback_url != 'oob' and oauth_callback_confirmed: import warnings - warnings.warn("oauth2 library doesn't support OAuth 1.0a type callback, but remote requires it") + warnings.warn("oauth2 library doesn't support OAuth 1.0a type callback, but remote requires it") oauth_callback_confirmed = False auth_url_params = { - 'oauth_token' : request_tokens['oauth_token'], + 'oauth_token': request_tokens['oauth_token'], } - + # Use old-style callback argument - if OAUTH_CALLBACK_IN_URL or (callback_url!='oob' and not oauth_callback_confirmed): + if OAUTH_CALLBACK_IN_URL or (callback_url != 'oob' and not oauth_callback_confirmed): auth_url_params['oauth_callback'] = callback_url - + request_tokens['auth_url'] = self.authenticate_url + '?' + urllib.urlencode(auth_url_params) - + return request_tokens def get_authorized_tokens(self): @@ -250,7 +252,7 @@ class Twython(object): """ resp, content = self.client.request(self.access_token_url, "GET") return dict(parse_qsl(content)) - + # ------------------------------------------------------------------------------------------------------------------------ # The following methods are all different in some manner or require special attention with regards to the Twitter API. # Because of this, we keep them separate from all the other endpoint definitions - ideally this should be change-able, @@ -259,10 +261,10 @@ class Twython(object): @staticmethod def constructApiURL(base_url, params): - return base_url + "?" + "&".join(["%s=%s" %(Twython.unicode2utf8(key), urllib.quote_plus(Twython.unicode2utf8(value))) for (key, value) in params.iteritems()]) + return base_url + "?" + "&".join(["%s=%s" % (Twython.unicode2utf8(key), urllib.quote_plus(Twython.unicode2utf8(value))) for (key, value) in params.iteritems()]) @staticmethod - def shortenURL(url_to_shorten, shortener = "http://is.gd/api.php", query = "longurl"): + def shortenURL(url_to_shorten, shortener="http://is.gd/api.php", query="longurl"): """shortenURL(url_to_shorten, shortener = "http://is.gd/api.php", query = "longurl") Shortens url specified by url_to_shorten. @@ -275,9 +277,9 @@ class Twython(object): content = urllib2.urlopen(shortener + "?" + urllib.urlencode({query: Twython.unicode2utf8(url_to_shorten)})).read() return content except HTTPError, e: - raise TwythonError("shortenURL() failed with a %s error code." % `e.code`) + raise TwythonError("shortenURL() failed with a %s error code." % e.code) - def bulkUserLookup(self, ids = None, screen_names = None, version = 1, **kwargs): + def bulkUserLookup(self, ids=None, screen_names=None, version=1, **kwargs): """ bulkUserLookup(self, ids = None, screen_names = None, version = 1, **kwargs) A method to do bulk user lookups against the Twitter API. Arguments (ids (numbers) / screen_names (strings)) should be flat Arrays that @@ -289,13 +291,13 @@ class Twython(object): kwargs['user_id'] = ','.join(map(str, ids)) if screen_names: kwargs['screen_name'] = ','.join(screen_names) - + lookupURL = Twython.constructApiURL("http://api.twitter.com/%d/users/lookup.json" % version, kwargs) try: - resp, content = self.client.request(lookupURL, "POST", headers = self.headers) + resp, content = self.client.request(lookupURL, "POST", headers=self.headers) return simplejson.loads(content.decode('utf-8')) except HTTPError, e: - raise TwythonError("bulkUserLookup() failed with a %s error code." % `e.code`, e.code) + raise TwythonError("bulkUserLookup() failed with a %s error code." % e.code, e.code) def search(self, **kwargs): """search(search_query, **kwargs) @@ -309,7 +311,7 @@ class Twython(object): """ searchURL = Twython.constructApiURL("http://search.twitter.com/search.json", kwargs) try: - resp, content = self.client.request(searchURL, "GET", headers = self.headers) + resp, content = self.client.request(searchURL, "GET", headers=self.headers) if int(resp.status) == 420: retry_wait_seconds = resp['retry-after'] @@ -320,7 +322,7 @@ class Twython(object): return simplejson.loads(content.decode('utf-8')) except HTTPError, e: - raise TwythonError("getSearchTimeline() failed with a %s error code." % `e.code`, e.code) + raise TwythonError("getSearchTimeline() failed with a %s error code." % e.code, e.code) def searchTwitter(self, **kwargs): """use search() ,this is a fall back method to support searchTwitter() @@ -340,10 +342,10 @@ class Twython(object): """ searchURL = Twython.constructApiURL("http://search.twitter.com/search.json?q=%s" % Twython.unicode2utf8(search_query), kwargs) try: - resp, content = self.client.request(searchURL, "GET", headers = self.headers) + resp, content = self.client.request(searchURL, "GET", headers=self.headers) data = simplejson.loads(content.decode('utf-8')) except HTTPError, e: - raise TwythonError("searchGen() failed with a %s error code." % `e.code`, e.code) + raise TwythonError("searchGen() failed with a %s error code." % e.code, e.code) if not data['results']: raise StopIteration @@ -360,9 +362,9 @@ class Twython(object): kwargs['page'] = str(kwargs['page']) except TypeError: raise TwythonError("searchGen() exited because page takes str") - except e: - raise TwythonError("searchGen() failed with %s error code" %\ - `e.code`, e.code) + except e: + raise TwythonError("searchGen() failed with %s error code" % \ + e.code, e.code) for tweet in self.searchGen(search_query, **kwargs): yield tweet @@ -372,7 +374,7 @@ class Twython(object): searchTwitterGen()""" return self.searchGen(search_query, **kwargs) - def isListMember(self, list_id, id, username, version = 1): + def isListMember(self, list_id, id_, username, version=1): """ isListMember(self, list_id, id, version) Check if a specified user (id) is a member of the list in question (list_id). @@ -386,12 +388,12 @@ class Twython(object): version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ try: - resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, username, list_id, `id`), headers = self.headers) + resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, username, list_id, id_), headers=self.headers) return simplejson.loads(content.decode('utf-8')) except HTTPError, e: raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) - def isListSubscriber(self, username, list_id, id, version = 1): + def isListSubscriber(self, username, list_id, id_, version=1): """ isListSubscriber(self, list_id, id, version) Check if a specified user (id) is a subscriber of the list in question (list_id). @@ -405,7 +407,7 @@ class Twython(object): version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ try: - resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, username, list_id, `id`), headers = self.headers) + resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, username, list_id, id_), headers=self.headers) return simplejson.loads(content.decode('utf-8')) except HTTPError, e: raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) @@ -421,7 +423,7 @@ class Twython(object): tile - Optional (defaults to True). If set to true the background image will be displayed tiled. The image will not be tiled otherwise. version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ - return self._media_update('http://api.twitter.com/%d/account/update_profile_background_image.json' % version, {'image':(file_, open(file_, 'rb'))}, params={'tile':tile}) + return self._media_update('http://api.twitter.com/%d/account/update_profile_background_image.json' % version, {'image': (file_, open(file_, 'rb'))}, params={'tile': tile}) def updateProfileImage(self, file_, version=1): """ updateProfileImage(filename) @@ -432,7 +434,7 @@ class Twython(object): image - Required. Must be a valid GIF, JPG, or PNG image of less than 700 kilobytes in size. Images with width larger than 500 pixels will be scaled down. version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ - return self._media_update('http://api.twitter.com/%d/account/update_profile_image.json' % version, {'image':(file_, open(file_, 'rb'))}) + return self._media_update('http://api.twitter.com/%d/account/update_profile_image.json' % version, {'image': (file_, open(file_, 'rb'))}) # statuses/update_with_media def updateStatusWithMedia(self, file_, version=1, **params): @@ -444,7 +446,7 @@ class Twython(object): image - Required. Must be a valid GIF, JPG, or PNG image of less than 700 kilobytes in size. Images with width larger than 500 pixels will be scaled down. version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ - return self._media_update('https://upload.twitter.com/%d/statuses/update_with_media.json' % version, {'media':(file_, open(file_, 'rb'))}, **params) + return self._media_update('https://upload.twitter.com/%d/statuses/update_with_media.json' % version, {'media': (file_, open(file_, 'rb'))}, **params) def _media_update(self, url, file_, params={}): oauth_params = { @@ -455,23 +457,22 @@ class Twython(object): #create a fake request with your upload url and parameters faux_req = oauth.Request(method='POST', url=url, parameters=oauth_params) - + #sign the fake request. signature_method = oauth.SignatureMethod_HMAC_SHA1() faux_req.sign_request(signature_method, self.consumer, self.token) - + #create a dict out of the fake request signed params self.headers.update(faux_req.to_header()) req = requests.post(url, data=params, files=file_, headers=self.headers) return req.content - def getProfileImageUrl(self, username, size=None, version=1): """ getProfileImageUrl(username) - + Gets the URL for the user's profile image. - + Parameters: username - Required. User name of the user you want the image url of. size - Optional. Image size. Valid options include 'normal', 'mini' and 'bigger'. Defaults to 'normal' if not given. @@ -479,19 +480,19 @@ class Twython(object): """ url = "http://api.twitter.com/%s/users/profile_image/%s.json" % (version, username) if size: - url = self.constructApiURL(url, {'size':size}) - + url = self.constructApiURL(url, {'size': size}) + client = httplib2.Http() client.follow_redirects = False resp, content = client.request(url, 'GET') - - if resp.status in (301,302,303,307): + + if resp.status in (301, 302, 303, 307): return resp['location'] elif resp.status == 200: return simplejson.loads(content.decode('utf-8')) - + raise TwythonError("getProfileImageUrl() failed with a %d error code." % resp.status, resp.status) - + @staticmethod def unicode2utf8(text): try: @@ -503,6 +504,6 @@ class Twython(object): @staticmethod def encode(text): - if isinstance(text, (str,unicode)): + if isinstance(text, (str, unicode)): return Twython.unicode2utf8(text) return str(text) From 3ff7dd8327e768f9c4d6872a0510fc5dc05baed7 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Fri, 24 Feb 2012 15:04:32 -0500 Subject: [PATCH 3/4] It's okay to use "id" as a var, my bad. --- twython/twython.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/twython/twython.py b/twython/twython.py index 2088f3b..6d38ac3 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -374,7 +374,7 @@ class Twython(object): searchTwitterGen()""" return self.searchGen(search_query, **kwargs) - def isListMember(self, list_id, id_, username, version=1): + def isListMember(self, list_id, id, username, version=1): """ isListMember(self, list_id, id, version) Check if a specified user (id) is a member of the list in question (list_id). @@ -388,12 +388,12 @@ class Twython(object): version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ try: - resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, username, list_id, id_), headers=self.headers) + resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, username, list_id, id), headers=self.headers) return simplejson.loads(content.decode('utf-8')) except HTTPError, e: raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) - def isListSubscriber(self, username, list_id, id_, version=1): + def isListSubscriber(self, username, list_id, id, version=1): """ isListSubscriber(self, list_id, id, version) Check if a specified user (id) is a subscriber of the list in question (list_id). @@ -407,7 +407,7 @@ class Twython(object): version (number) - Optional. API version to request. Entire Twython class defaults to 1, but you can override on a function-by-function or class basis - (version=2), etc. """ try: - resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, username, list_id, id_), headers=self.headers) + resp, content = self.client.request("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, username, list_id, id), headers=self.headers) return simplejson.loads(content.decode('utf-8')) except HTTPError, e: raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) From 4553e273c7d71fe11306d6a29d785beaec055066 Mon Sep 17 00:00:00 2001 From: Michael Helmick Date: Tue, 28 Feb 2012 14:48:35 -0500 Subject: [PATCH 4/4] Fixes #59 --- twython/twython.py | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/twython/twython.py b/twython/twython.py index 6d38ac3..090183a 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -15,7 +15,6 @@ import urllib import urllib2 import httplib2 import re -import inspect import time import requests @@ -50,20 +49,6 @@ except ImportError: # Seriously wtf is wrong with you if you get this Exception. raise Exception("Twython requires the simplejson library (or Python 2.6) to work. http://www.undefined.org/python/") -# Try and gauge the old OAuth2 library spec. Versions 1.5 and greater no longer have the callback -# url as part of the request object; older versions we need to patch for Python 2.5... ugh. ;P -OAUTH_CALLBACK_IN_URL = False -OAUTH_LIB_SUPPORTS_CALLBACK = False -if not hasattr(oauth, '_version') or float(oauth._version.manual_verstr) <= 1.4: - OAUTH_CLIENT_INSPECTION = inspect.getargspec(oauth.Client.request) - try: - OAUTH_LIB_SUPPORTS_CALLBACK = 'callback_url' in OAUTH_CLIENT_INSPECTION.args - except AttributeError: - # Python 2.5 doesn't return named tuples, so don't look for an args section specifically. - OAUTH_LIB_SUPPORTS_CALLBACK = 'callback_url' in OAUTH_CLIENT_INSPECTION -else: - OAUTH_CALLBACK_IN_URL = True - class TwythonError(AttributeError): """ @@ -124,7 +109,7 @@ class TwythonAuthError(TwythonError): class Twython(object): def __init__(self, twitter_token=None, twitter_secret=None, oauth_token=None, oauth_token_secret=None, \ - headers=None, callback_url=None, client_args=None): + headers=None, client_args=None): """setup(self, oauth_token = None, headers = None) Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below). @@ -150,7 +135,6 @@ class Twython(object): self.twitter_secret = twitter_secret self.oauth_token = oauth_token self.oauth_secret = oauth_token_secret - self.callback_url = callback_url # If there's headers, set them, otherwise be an embarassing parent for their own good. self.headers = headers @@ -212,11 +196,7 @@ class Twython(object): Returns an authorization URL for a user to hit. """ - callback_url = self.callback_url or 'oob' - request_args = {} - if OAUTH_LIB_SUPPORTS_CALLBACK: - request_args['callback_url'] = callback_url resp, content = self.client.request(self.request_token_url, "GET", **request_args) @@ -225,21 +205,10 @@ class Twython(object): request_tokens = dict(parse_qsl(content)) - oauth_callback_confirmed = request_tokens.get('oauth_callback_confirmed') == 'true' - - if not OAUTH_LIB_SUPPORTS_CALLBACK and callback_url != 'oob' and oauth_callback_confirmed: - import warnings - warnings.warn("oauth2 library doesn't support OAuth 1.0a type callback, but remote requires it") - oauth_callback_confirmed = False - auth_url_params = { 'oauth_token': request_tokens['oauth_token'], } - # Use old-style callback argument - if OAUTH_CALLBACK_IN_URL or (callback_url != 'oob' and not oauth_callback_confirmed): - auth_url_params['oauth_callback'] = callback_url - request_tokens['auth_url'] = self.authenticate_url + '?' + urllib.urlencode(auth_url_params) return request_tokens @@ -507,3 +476,15 @@ class Twython(object): if isinstance(text, (str, unicode)): return Twython.unicode2utf8(text) return str(text) + +if __name__ == '__main__': + t = Twython('OZpEzBbXBin6U0BnZNBTQ', 'PyK5bJG68qDkVBtI5xTghkY1BvQvZTB2JtlqxJjzE') + req_tokens = t.get_authentication_tokens() + oauth_secret = req_tokens['oauth_token_secret'] + print 'Go to %s' % req_tokens['auth_url'] + + oauth_token = raw_input('OAuth Token: ') + + t = Twython('OZpEzBbXBin6U0BnZNBTQ', 'PyK5bJG68qDkVBtI5xTghkY1BvQvZTB2JtlqxJjzE', oauth_token, oauth_secret) + final_tokens = t.get_authorized_tokens() + print final_tokens