From b491faf757a0ab4f39d2ecd8b37c634130913b7c Mon Sep 17 00:00:00 2001 From: Ryan McGrath Date: Thu, 10 Dec 2009 03:12:24 -0500 Subject: [PATCH] Proxy authentication support for Twython. Experimental, needs testing - pass a proxy object (username/password/host/port) to the setup method, Twython will try and route everything through the proxy and properly handle things. Headers can now be added to un-authed requests; proxies also work with both authed/un-authed requests. --- twython.py | 60 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/twython.py b/twython.py index ffe31e5..5c2de4b 100644 --- a/twython.py +++ b/twython.py @@ -52,8 +52,8 @@ class AuthError(TwythonError): return repr(self.msg) class setup: - def __init__(self, username = None, password = None, headers = None, version = 1): - """setup(authtype = "OAuth", username = None, password = None, consumer_secret = None, consumer_key = None, headers = None) + def __init__(self, username = None, password = None, headers = None, proxy = None, version = 1): + """setup(authtype = "OAuth", username = None, password = None, proxy = None, headers = None) Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below). @@ -68,13 +68,19 @@ class setup: self.authenticated = False self.username = username self.apiVersion = version + self.proxy = proxy + if self.proxy is not None: + self.proxyobj = urllib2.ProxyHandler({'http': 'http://%s:%s@%s:%d' % (self.proxy["username"], self.proxy["password"], self.proxy["host"], self.proxy["port"])}) # Check and set up authentication if self.username is not None and password is not None: # Assume Basic authentication ritual self.auth_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() self.auth_manager.add_password(None, "http://api.twitter.com", self.username, password) self.handler = urllib2.HTTPBasicAuthHandler(self.auth_manager) - self.opener = urllib2.build_opener(self.handler) + if self.proxy is not None: + self.opener = urllib2.build_opener(self.proxyobj, self.handler) + else: + self.opener = urllib2.build_opener(self.handler) if headers is not None: self.opener.addheaders = [('User-agent', headers)] try: @@ -83,7 +89,13 @@ class setup: except HTTPError, e: raise AuthError("Authentication failed with your provided credentials. Try again? (%s failure)" % `e.code`) else: - pass + # Build a non-auth opener so we can allow proxy-auth and/or header swapping + if self.proxy is not None: + self.opener = urllib2.build_opener(self.proxyobj) + else: + self.opener = urllib2.build_opener() + if self.headers is not None: + self.opener.addheaders = [('User-agent', headers)] # URL Shortening function huzzah def shortenURL(self, url_to_shorten, shortener = "http://is.gd/api.php", query = "longurl"): @@ -119,7 +131,7 @@ class setup: version = version or self.apiVersion try: if rate_for == "requestingIP": - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/account/rate_limit_status.json" % version)) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/account/rate_limit_status.json" % version)) else: if self.authenticated is True: return simplejson.load(self.opener.open("http://api.twitter.com/%d/account/rate_limit_status.json" % version)) @@ -139,7 +151,7 @@ class setup: """ version = version or self.apiVersion try: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/statuses/public_timeline.json" % version)) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/statuses/public_timeline.json" % version)) except HTTPError, e: raise TwythonError("getPublicTimeline() failed with a %s error code." % `e.code`) @@ -223,7 +235,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open(userTimelineURL)) else: - return simplejson.load(urllib2.urlopen(userTimelineURL)) + return simplejson.load(self.opener.open(userTimelineURL)) except HTTPError, e: raise TwythonError("Failed with a %s error code. Does this user hide/protect their updates? If so, you'll need to authenticate and be their friend to get their timeline." % `e.code`, e.code) @@ -440,7 +452,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open(apiURL)) else: - return simplejson.load(urllib2.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("showUser() failed with a %s error code." % `e.code`, e.code) @@ -537,7 +549,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open("http://api.twitter.com/%d/statuses/show/%s.json" % (version, id))) else: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/statuses/show/%s.json" % (version, id))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/statuses/show/%s.json" % (version, id))) except HTTPError, e: raise TwythonError("Failed with a %s error code. Does this user hide/protect their updates? You'll need to authenticate and be friends to get their timeline." % `e.code`, e.code) @@ -830,7 +842,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open(apiURL)) else: - return simplejson.load(urllib2.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: # Catch this for now if e.code == 403: @@ -1092,7 +1104,7 @@ class setup: if screen_name is not None: apiURL = "http://api.twitter.com/%d/friends/ids.json?screen_name=%s&%s" %(version, screen_name, breakResults) try: - return simplejson.load(urllib2.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("getFriendsIDs() failed with a %s error code." % `e.code`, e.code) @@ -1124,7 +1136,7 @@ class setup: if screen_name is not None: apiURL = "http://api.twitter.com/%d/followers/ids.json?screen_name=%s&%s" %(version, screen_name, breakResults) try: - return simplejson.load(urllib2.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("getFollowersIDs() failed with a %s error code." % `e.code`, e.code) @@ -1188,7 +1200,7 @@ class setup: if screen_name is not None: apiURL = "http://api.twitter.com/%d/blocks/exists.json?screen_name=%s" % (version, screen_name) try: - return simplejson.load(urllib2.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("checkIfBlockExists() failed with a %s error code." % `e.code`, e.code) @@ -1254,7 +1266,7 @@ class setup: """ searchURL = self.constructApiURL("http://search.twitter.com/search.json", kwargs) + "&" + urllib.urlencode({"q": self.unicode2utf8(search_query)}) try: - return simplejson.load(urllib2.urlopen(searchURL)) + return simplejson.load(self.opener.open(searchURL)) except HTTPError, e: raise TwythonError("getSearchTimeline() failed with a %s error code." % `e.code`, e.code) @@ -1271,7 +1283,7 @@ class setup: if excludeHashTags is True: apiURL += "?exclude=hashtags" try: - return simplejson.load(urllib.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("getCurrentTrends() failed with a %s error code." % `e.code`, e.code) @@ -1295,7 +1307,7 @@ class setup: else: apiURL += "?exclude=hashtags" try: - return simplejson.load(urllib.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("getDailyTrends() failed with a %s error code." % `e.code`, e.code) @@ -1319,7 +1331,7 @@ class setup: else: apiURL += "?exclude=hashtags" try: - return simplejson.load(urllib.urlopen(apiURL)) + return simplejson.load(self.opener.open(apiURL)) except HTTPError, e: raise TwythonError("getWeeklyTrends() failed with a %s error code." % `e.code`, e.code) @@ -1531,7 +1543,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/lists/%s/statuses.json" % (version, self.username, list_id))) else: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/%s/lists/%s/statuses.json" % (version, self.username, list_id))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/lists/%s/statuses.json" % (version, self.username, list_id))) except HTTPError, e: if e.code == 404: raise AuthError("It seems the list you're trying to access is private/protected, and you don't have access. Are you authenticated and allowed?") @@ -1570,7 +1582,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/%s/members.json" % (version, self.username, list_id))) else: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/%s/%s/members.json" % (version, self.username, list_id))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/%s/members.json" % (version, self.username, list_id))) except HTTPError, e: raise TwythonError("getListMembers() failed with a %d error code." % e.code, e.code) @@ -1610,7 +1622,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, self.username, list_id, `id`))) else: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, self.username, list_id, `id`))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/%s/members/%s.json" % (version, self.username, list_id, `id`))) except HTTPError, e: raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) @@ -1665,7 +1677,7 @@ class setup: if self.authenticated is True: return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, self.username, list_id, `id`))) else: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, self.username, list_id, `id`))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, self.username, list_id, `id`))) except HTTPError, e: raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) @@ -1684,8 +1696,8 @@ class setup: version = version or self.apiVersion try: if latitude is not None and longitude is not None: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/trends/available.json?latitude=%s&longitude=%s" % (version, latitude, longitude))) - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/trends/available.json" % version)) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/trends/available.json?latitude=%s&longitude=%s" % (version, latitude, longitude))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/trends/available.json" % version)) except HTTPError, e: raise TwythonError("availableTrends() failed with a %d error code." % e.code, e.code) @@ -1702,7 +1714,7 @@ class setup: """ version = version or self.apiVersion try: - return simplejson.load(urllib2.urlopen("http://api.twitter.com/%d/trends/%s.json" % (version, woeid))) + return simplejson.load(self.opener.open("http://api.twitter.com/%d/trends/%s.json" % (version, woeid))) except HTTPError, e: raise TwythonError("trendsByLocation() failed with a %d error code." % e.code, e.code)