diff --git a/setup.py b/setup.py index 1f28737..7618ec2 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#! /usr/bin/python import sys, os from setuptools import setup @@ -25,7 +25,8 @@ setup( license = 'MIT License', url = 'http://github.com/ryanmcgrath/twython/tree/master', keywords = 'twitter search api tweet twython', - description = 'An easy (and up to date) way to access Twitter data with Python.', + description = 'An easy (and up to date) way to access Twitter data with\ + Python.', long_description = open('README.markdown').read(), classifiers = [ 'Development Status :: 4 - Beta', diff --git a/twython/streaming.py b/twython/streaming.py index d145bf3..84f7876 100644 --- a/twython/streaming.py +++ b/twython/streaming.py @@ -29,15 +29,18 @@ try: import json as simplejson except ImportError: try: - # Python 2.6 and below (2.4/2.5, 2.3 is not guranteed to work with this library to begin with) + # Python 2.6 and below (2.4/2.5, 2.3 is not guranteed to work with\ + # this library to begin with) import simplejson except ImportError: try: - # This case gets rarer by the day, but if we need to, we can pull it from Django provided it's there. + # This case gets rarer by the day, but if we need to, \ + # we can pull it from Django provided it's there. from django.utils import simplejson except: # 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/") + raise Exception("Twython requires the simplejson library \ + (or Python 2.6) to work. http://www.undefined.org/python/") class TwythonStreamingError(Exception): def __init__(self, msg): @@ -57,5 +60,6 @@ feeds = { } class Stream(object): - def __init__(self, username = None, password = None, feed = "spritzer", user_agent = "Twython Streaming"): + def __init__(self, username = None, password = None, feed = "spritzer", \ + user_agent = "Twython Streaming"): pass diff --git a/twython/twitter_endpoints.py b/twython/twitter_endpoints.py index 6ad15bc..fe13f63 100644 --- a/twython/twitter_endpoints.py +++ b/twython/twitter_endpoints.py @@ -1,15 +1,19 @@ """ - A huge map of every Twitter API endpoint to a function definition in Twython. + A huge map of every Twitter API endpoint to a function definition in \ + Twython. - Parameters that need to be embedded in the URL are treated with mustaches, e.g: + Parameters that need to be embedded in the URL are treated with mustaches,\ + e.g: {{version}}, etc - When creating new endpoint definitions, keep in mind that the name of the mustache - will be replaced with the keyword that gets passed in to the function at call time. + When creating new endpoint definitions, keep in mind that the name of the\ + mustache will be replaced with the keyword that gets passed in to the \ + function at call time. - i.e, in this case, if I pass version = 47 to any function, {{version}} will be replaced - with 47, instead of defaulting to 1 (said defaulting takes place at conversion time). + i.e, in this case, if I pass version = 47 to any function, {{version}} \ + will be replaced with 47, instead of defaulting to 1\ + (said defaulting takes place at conversion time). """ # Base Twitter API url, no need to repeat this junk... diff --git a/twython/twython.py b/twython/twython.py index bc77526..ec40cf2 100644 --- a/twython/twython.py +++ b/twython/twython.py @@ -1,9 +1,9 @@ -#!/usr/bin/python +#! /usr/bin/python """ Twython is a library for Python that wraps the Twitter API. - It aims to abstract away all the API endpoints, so that additions to the library - and/or the Twitter API won't cause any overall problems. + It aims to abstract away all the API endpoints, so that additions to the\ + library and/or the Twitter API won't cause any overall problems. Questions, comments? ryan@venodesigns.net """ @@ -38,26 +38,33 @@ try: import json as simplejson except ImportError: try: - # Python 2.6 and below (2.4/2.5, 2.3 is not guranteed to work with this library to begin with) + # Python 2.6 and below (2.4/2.5, 2.3 is not guranteed to work with \ + # this library to begin with) import simplejson except ImportError: try: - # This case gets rarer by the day, but if we need to, we can pull it from Django provided it's there. + # This case gets rarer by the day, but if we need to, we can pull \ + # it from Django provided it's there. from django.utils import simplejson except: # 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/") + 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 -# 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 + 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. + # 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 @@ -67,8 +74,8 @@ class TwythonError(AttributeError): Generic error class, catch-all for most Twython issues. Special cases are handled by APILimit and AuthError. - 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: + 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 """ @@ -84,8 +91,8 @@ class TwythonError(AttributeError): class APILimit(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 - this matter beyond telling you that you've done goofed. + docs if you're running into issues here, Twython does not concern \ + itself with this matter beyond telling you that you've done goofed. """ def __init__(self, msg): self.msg = msg @@ -96,8 +103,8 @@ class APILimit(TwythonError): class AuthError(TwythonError): """ - Raised when you try to access a protected resource and it fails due to some issue with - your authentication. + Raised when you try to access a protected resource and it fails due \ + to some issue with your authentication. """ def __init__(self, msg): self.msg = msg @@ -107,21 +114,35 @@ class AuthError(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={}): """setup(self, oauth_token = None, headers = None) - Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below). + Instantiates an instance of Twython. Takes optional parameters for \ + authentication and such (see below). Parameters: - twitter_token - Given to you when you register your application with Twitter. - twitter_secret - Given to you when you register your application with Twitter. - oauth_token - If you've gone through the authentication process and have a token for this user, - pass it in and it'll be used for all requests going forward. - oauth_token_secret - see oauth_token; it's the other half. - 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} + twitter_token - Given to you when you register your \ + application with Twitter. - ** Note: versioning is not currently used by search.twitter functions; when Twitter moves their junk, it'll be supported. + twitter_secret - Given to you when you register your \ + application with Twitter. + + oauth_token - If you've gone through the authentication process\ + and have a token for this user,\pass it in and + it'll be used for all requests going forward. + + oauth_token_secret - see oauth_token; it's the other half. + + headers - User agent header, dictionary style aka + \{'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. """ # Needed for hitting that there API. self.request_token_url = 'http://twitter.com/oauth/request_token' @@ -134,7 +155,8 @@ class Twython(object): 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. + # If there's headers, set them, otherwise be an embarassing parent for \ + # their own good. self.headers = headers if self.headers is None: self.headers = {'User-agent': 'Twython Python Twitter Library v1.3'} @@ -148,46 +170,63 @@ class Twython(object): if self.oauth_token is not None and self.oauth_secret is not None: token = oauth.Token(oauth_token, oauth_token_secret) - # Filter down through the possibilities here - if they have a token, if they're first stage, etc. + # Filter down through the possibilities here - if they have a token,\ + # if they're first stage, etc. if consumer is not None and token is not None: self.client = oauth.Client(consumer, token, **client_args) elif consumer is not None: self.client = oauth.Client(consumer, **client_args) else: - # If they don't do authentication, but still want to request unprotected resources, we need an opener. + # If they don't do authentication, but still want to request \ + # unprotected resources, we need an opener. self.client = httplib2.Http(**client_args) def __getattr__(self, api_call): """ The most magically awesome block of code you'll see in 2010. - Rather than list out 9 million damn methods for this API, we just keep a table (see above) of - every API endpoint and their corresponding function id for this library. This pretty much gives - unlimited flexibility in API support - there's a slight chance of a performance hit here, but if this is - going to be your bottleneck... well, don't use Python. ;P + Rather than list out 9 million damn methods for this API, we just \ + keep a table (see above) of every API endpoint and their \ + corresponding function id for this library. This pretty much gives + unlimited flexibility in API support - there's a slight chance of a\ + performance hit here, but if this is going to be your bottleneck...\ + well, don't use Python. ;P - For those who don't get what's going on here, Python classes have this great feature known as __getattr__(). - It's called when an attribute that was called on an object doesn't seem to exist - since it doesn't exist, - we can take over and find the API method in our table. We then return a function that downloads and parses - what we're looking for, based on the keywords passed in. + For those who don't get what's going on here, Python classes have \ + this great feature known as __getattr__(). + + It's called when an attribute that was called on an object doesn't \ + seem to exist - since it doesn't exist, + + we can take over and find the API method in our table. We then \ + return a function that downloads and parses what we're looking for,\ + based on the keywords passed in. - I'll hate myself for saying this, but this is heavily inspired by Ruby's "method_missing". + I'll hate myself for saying this, but this is heavily inspired by \ + Ruby's "method_missing". """ def get(self, **kwargs): # Go through and replace any mustaches that are in our API url. fn = api_table[api_call] base = re.sub( '\{\{(?P[a-zA-Z_]+)\}\}', - lambda m: "%s" % kwargs.get(m.group(1), '1'), # The '1' here catches the API version. Slightly hilarious. + lambda m: "%s" % kwargs.get(m.group(1), '1'),\ + # The '1' here catches the API version. Slightly hilarious. base_url + fn['url'] ) - # Then open and load that shiiit, yo. TODO: check HTTP method and junk, handle errors/authentication + # Then open and load that shiiit, yo. + # TODO: check HTTP method and junk, handle errors/authentication + if fn['method'] == 'POST': - resp, content = self.client.request(base, fn['method'], urllib.urlencode(dict([k, Twython.encode(v)] for k, v in kwargs.items())), headers = self.headers) + resp, content = self.client.request(base, fn['method'],\ + urllib.urlencode(dict([k, Twython.encode(v)] \ + for k, v in kwargs.items())), headers = self.headers) else: - url = base + "?" + "&".join(["%s=%s" %(key, value) for (key, value) in kwargs.iteritems()]) - resp, content = self.client.request(url, fn['method'], headers = self.headers) + url = base + "?" + "&".join(["%s=%s" %(key, value)\ + for (key, value) in kwargs.iteritems()]) + resp, content = self.client.request(url, fn['method'],\ + headers = self.headers) return simplejson.loads(content) @@ -208,21 +247,26 @@ class Twython(object): if OAUTH_LIB_SUPPORTS_CALLBACK: request_args['callback_url'] = callback_url - resp, content = self.client.request(self.request_token_url, "GET", **request_args) + 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 AuthError("Seems something couldn't be verified with your\ + OAuth junk. Error: %s, Message: %s" % (resp['status'], content)) try: request_tokens = dict(urlparse.parse_qsl(content)) except: request_tokens = dict(cgi.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: + 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 = { @@ -230,10 +274,12 @@ class Twython(object): } # 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) + request_tokens['auth_url'] = self.authenticate_url + '?' + \ + urllib.urlencode(auth_url_params) return request_tokens @@ -249,51 +295,67 @@ class Twython(object): except: return dict(cgi.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, - # but it's not high on the priority list at the moment. - # ------------------------------------------------------------------------------------------------------------------------ + # -------------------------------------------------------------------------- + # 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, but it's not high on \ + # the priority list at the moment. + # -------------------------------------------------------------------------- @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"): - """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. Parameters: url_to_shorten - URL to shorten. - shortener - In case you want to use a url shortening service other than is.gd. + shortener - In case you want to use a url shortening service \ + other than is.gd. """ try: - content = urllib2.urlopen(shortener + "?" + urllib.urlencode({query: Twython.unicode2utf8(url_to_shorten)})).read() + 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): - """ 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 - contain their respective data sets. + A method to do bulk user lookups against the Twitter API.\ + Arguments (ids (numbers) / screen_names (strings)) should be flat \ + Arrays that contain their respective data sets. - Statuses for the users in question will be returned inline if they exist. Requires authentication! + Statuses for the users in question will be returned inline if they \ + exist. Requires authentication! """ if ids: 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) + 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) 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) @@ -301,16 +363,21 @@ class Twython(object): Returns tweets that match a specified query. Parameters: - See the documentation at http://dev.twitter.com/doc/get/search. Pass in the API supported arguments as named parameters. + See the documentation at http://dev.twitter.com/doc/get/search.\ + Pass in the API supported arguments as named parameters. - e.g x.search(q = "jjndf", page = '2') + e.g x.search(q = "python", page = '2') + e.g x.search("python", page = '2') """ - searchURL = Twython.constructApiURL("http://search.twitter.com/search.json", kwargs) + 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) return simplejson.loads(content) except HTTPError, e: - raise TwythonError("getSearchTimeline() failed with a %s error code." % `e.code`, e.code) + raise TwythonError("getSearchTimeline() failed with %s error code"\ + % `e.code`, e.code) def searchTwitter(self, **kwargs): """use search() ,this is a fall back method to support searchTwitter() @@ -323,17 +390,22 @@ class Twython(object): Returns a generator of tweets that match a specified query. Parameters: - See the documentation at http://dev.twitter.com/doc/get/search. Pass in the API supported arguments as named parameters. + See the documentation at http://dev.twitter.com/doc/get/search.\ + Pass in the API supported arguments as named parameters. e.g x.searchGen("python", page="2") or x.searchGen(search_query = "python", page = "2") """ - searchURL = Twython.constructApiURL("http://search.twitter.com/search.json?q=%s" % Twython.unicode2utf8(search_query), kwargs) + 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) 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 @@ -365,62 +437,94 @@ class Twython(object): 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). + Check if a specified user (id) is a member of the list in question \ + (list_id). - **Note: This method may not work for private/protected lists, unless you're authenticated and have access to those lists. + **Note: This method may not work for private/protected lists, \ + unless you're authenticated and have access to those lists. Parameters: list_id - Required. The slug of the list to check against. id - Required. The ID of the user being checked in the list. - username - User who owns the list you're checking against (username) - 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. + username - User who owns the list you're checking against \ + (username) 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) except HTTPError, e: - raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) + raise TwythonError("isListMember() failed with a %d error code." % \ + e.code, e.code) 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). + Check if a specified user (id) is a subscriber of the list in \ + question (list_id). - **Note: This method may not work for private/protected lists, unless you're authenticated and have access to those lists. + **Note: This method may not work for private/protected lists, \ + unless you're authenticated and have access to those lists. Parameters: list_id - Required. The slug of the list to check against. id - Required. The ID of the user being checked in the list. - username - Required. The username of the owner of the list that you're seeing if someone is subscribed to. - 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. + username - Required. The username of the owner of the list \ + that you're seeing if someone is subscribed to. + + 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) except HTTPError, e: - raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) + raise TwythonError("isListMember() failed with a %d error code." % \ + e.code, e.code) - # The following methods are apart from the other Account methods, because they rely on a whole multipart-data posting function set. + # The following methods are apart from the other Account methods, \ + # because they rely on a whole multipart-data posting function set. def updateProfileBackgroundImage(self, filename, tile="true", version = 1): """ updateProfileBackgroundImage(filename, tile="true") Updates the authenticating user's profile background image. Parameters: - image - Required. Must be a valid GIF, JPG, or PNG image of less than 800 kilobytes in size. Images with width larger than 2048 pixels will be forceably scaled down. - tile - Optional (defaults to true). If set to true the background image will be displayed tiled. The image will not be tiled otherwise. - ** Note: It's sad, but when using this method, pass the tile value as a string, e.g tile="false" - 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. + image - Required. Must be a valid GIF, JPG, or PNG image of \ + less than 800 kilobytes in size. Images with width larger than \ + 2048 pixels will be forceably scaled down. + + tile - Optional (defaults to true). If set to true the \ + background image will be displayed tiled. + The image will not be tiled otherwise. + + ** Note: It's sad, but when using this method, pass the tile \ + value as a string, + e.g tile="false" + 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: files = [("image", filename, open(filename, 'rb').read())] fields = [] - content_type, body = Twython.encode_multipart_formdata(fields, files) - headers = {'Content-Type': content_type, 'Content-Length': str(len(body))} - r = urllib2.Request("http://api.twitter.com/%d/account/update_profile_background_image.json?tile=%s" % (version, tile), body, headers) + content_type, body = Twython.encode_multipart_formdata(fields,\ + files) + headers = {'Content-Type': content_type, 'Content-Length': \ + str(len(body))} + r = urllib2.Request("http://api.twitter.com/%d/account/\ + update_profile_background_image.json?tile=%s" % (version, tile), \ + body, headers) return urllib2.urlopen(r).read() except HTTPError, e: - raise TwythonError("updateProfileBackgroundImage() failed with a %d error code." % e.code, e.code) + raise TwythonError("updateProfileBackgroundImage() failed with a \ + %d error code." % e.code, e.code) def updateProfileImage(self, filename, version = 1): """ updateProfileImage(filename) @@ -428,18 +532,28 @@ class Twython(object): Updates the authenticating user's profile image (avatar). Parameters: - 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. + 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. """ try: files = [("image", filename, open(filename, 'rb').read())] fields = [] - content_type, body = Twython.encode_multipart_formdata(fields, files) - headers = {'Content-Type': content_type, 'Content-Length': str(len(body))} - r = urllib2.Request("http://api.twitter.com/%d/account/update_profile_image.json" % version, body, headers) + content_type, body = Twython.encode_multipart_formdata(fields, \ + files) + headers = {'Content-Type': content_type, 'Content-Length': \ + str(len(body))} + r = urllib2.Request(\ + "http://api.twitter.com/%d/account/update_profile_image.json" %\ + version, body, headers) return urllib2.urlopen(r).read() except HTTPError, e: - raise TwythonError("updateProfileImage() failed with a %d error code." % e.code, e.code) + raise TwythonError("updateProfileImage() failed with a %d error \ + code" % `e.code`, e.code) def getProfileImageUrl(self, username, size=None, version=1): """ getProfileImageUrl(username) @@ -447,11 +561,18 @@ class Twython(object): 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. - 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. + 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. + + 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. """ - url = "http://api.twitter.com/%s/users/profile_image/%s.json" % (version, username) + url = "http://api.twitter.com/%s/users/profile_image/%s.json" %\ + (version, username) if size: url = self.constructApiURL(url, {'size':size}) @@ -464,7 +585,8 @@ class Twython(object): elif resp.status == 200: return simplejson.loads(content) - raise TwythonError("getProfileImageUrl() failed with a %d error code." % resp.status, resp.status) + raise TwythonError("getProfileImageUrl() failed with a %d error code." \ + % resp.status, resp.status) @staticmethod def encode_multipart_formdata(fields, files): @@ -478,8 +600,10 @@ class Twython(object): L.append(value) for (key, filename, value) in files: L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) - L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0] or 'application/octet-stream') + L.append('Content-Disposition: form-data; name="%s"; filename="%s"'\ + % (key, filename)) + L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0] or \ + 'application/octet-stream') L.append('') L.append(value) L.append('--' + BOUNDARY + '--') diff --git a/twython3k/twitter_endpoints.py b/twython3k/twitter_endpoints.py index 42bd5a1..1cd3d10 100644 --- a/twython3k/twitter_endpoints.py +++ b/twython3k/twitter_endpoints.py @@ -1,15 +1,19 @@ """ - A huge map of every Twitter API endpoint to a function definition in Twython. + A huge map of every Twitter API endpoint to a function definition in \ + Twython. - Parameters that need to be embedded in the URL are treated with mustaches, e.g: + Parameters that need to be embedded in the URL are treated with mustaches, \ + e.g: {{version}}, etc - When creating new endpoint definitions, keep in mind that the name of the mustache - will be replaced with the keyword that gets passed in to the function at call time. + When creating new endpoint definitions, keep in mind that the name of the \ + mustache will be replaced with the keyword that gets passed in to the \ + function at call time. - i.e, in this case, if I pass version = 47 to any function, {{version}} will be replaced - with 47, instead of defaulting to 1 (said defaulting takes place at conversion time). + i.e, in this case, if I pass version = 47 to any function, {{version}} will\ + be replaced with 47, instead of defaulting to 1 (said defaulting takes \ + place at conversion time). """ # Base Twitter API url, no need to repeat this junk... diff --git a/twython3k/twython.py b/twython3k/twython.py index 1cf3e34..3bf6b3c 100644 --- a/twython3k/twython.py +++ b/twython3k/twython.py @@ -1,9 +1,9 @@ -#!/usr/bin/python +#! /usr/bin/python """ Twython is a library for Python that wraps the Twitter API. - It aims to abstract away all the API endpoints, so that additions to the library - and/or the Twitter API won't cause any overall problems. + It aims to abstract away all the API endpoints, so that additions to the \ + library and/or the Twitter API won't cause any overall problems. Questions, comments? ryan@venodesigns.net """ @@ -35,8 +35,10 @@ class TwythonError(AttributeError): Generic error class, catch-all for most Twython issues. Special cases are handled by APILimit and AuthError. - 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: + 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 """ @@ -52,8 +54,8 @@ class TwythonError(AttributeError): class APILimit(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 - this matter beyond telling you that you've done goofed. + docs if you're running into issues here, Twython does not concern \ + itself with this matter beyond telling you that you've done goofed. """ def __init__(self, msg): self.msg = msg @@ -64,8 +66,8 @@ class APILimit(TwythonError): class AuthError(TwythonError): """ - Raised when you try to access a protected resource and it fails due to some issue with - your authentication. + Raised when you try to access a protected resource and it fails due to \ + some issue with your authentication. """ def __init__(self, msg): self.msg = msg @@ -75,20 +77,30 @@ class AuthError(TwythonError): class Twython(object): - def __init__(self, twitter_token = None, twitter_secret = None, oauth_token = None, oauth_token_secret = None, headers=None): + def __init__(self, twitter_token = None, twitter_secret = None,\ + oauth_token = None, oauth_token_secret = None, headers=None): """setup(self, oauth_token = None, headers = None) - Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below). + Instantiates an instance of Twython. Takes optional parameters for \ + authentication and such (see below). Parameters: - twitter_token - Given to you when you register your application with Twitter. - twitter_secret - Given to you when you register your application with Twitter. - oauth_token - If you've gone through the authentication process and have a token for this user, - pass it in and it'll be used for all requests going forward. - oauth_token_secret - see oauth_token; it's the other half. - headers - User agent header, dictionary style ala {'User-Agent': 'Bert'} + twitter_token - Given to you when you register your application\ + with Twitter. - ** Note: versioning is not currently used by search.twitter functions; when Twitter moves their junk, it'll be supported. + twitter_secret - Given to you when you register your \ + application with Twitter. + + oauth_token - If you've gone through the authentication process\ + and have a token for this user,pass it in and \ + it'll be used for all requests going forward. + + oauth_token_secret - see oauth_token; it's the other half. + headers - User agent header, dictionary style aka \ + {'User-Agent': 'Bert'} + + ** 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' @@ -100,7 +112,8 @@ class Twython(object): self.oauth_token = oauth_token self.oauth_secret = oauth_token_secret - # If there's headers, set them, otherwise be an embarassing parent for their own good. + # If there's headers, set them, otherwise be an embarassing parent for \ + # their own good. self.headers = headers if self.headers is None: self.headers = {'User-agent': 'Twython Python Twitter Library v1.3'} @@ -114,45 +127,59 @@ class Twython(object): if self.oauth_token is not None and self.oauth_secret is not None: token = oauth.Token(oauth_token, oauth_token_secret) - # Filter down through the possibilities here - if they have a token, if they're first stage, etc. + # Filter down through the possibilities here - if they have a token, \ + # if they're first stage, etc. if consumer is not None and token is not None: self.client = oauth.Client(consumer, token) elif consumer is not None: self.client = oauth.Client(consumer) else: - # If they don't do authentication, but still want to request unprotected resources, we need an opener. + # If they don't do authentication, but still want to request \ + # unprotected resources, we need an opener. self.client = httplib2.Http() def __getattr__(self, api_call): """ The most magically awesome block of code you'll see in 2010. - Rather than list out 9 million damn methods for this API, we just keep a table (see above) of - every API endpoint and their corresponding function id for this library. This pretty much gives - unlimited flexibility in API support - there's a slight chance of a performance hit here, but if this is - going to be your bottleneck... well, don't use Python. ;P + Rather than list out 9 million damn methods for this API, \ + we just keep a table (see above) of every API endpoint and their \ + corresponding function id for this library. This pretty much gives + unlimited flexibility in API support - there's a slight chance of a\ + performance hit here, but if this is going to be your bottleneck...\ + well, don't use Python. ;P - For those who don't get what's going on here, Python classes have this great feature known as __getattr__(). - It's called when an attribute that was called on an object doesn't seem to exist - since it doesn't exist, - we can take over and find the API method in our table. We then return a function that downloads and parses - what we're looking for, based on the keywords passed in. + For those who don't get what's going on here, Python classes have \ + this great feature known as __getattr__(). - I'll hate myself for saying this, but this is heavily inspired by Ruby's "method_missing". + It's called when an attribute that was called on an object doesn't \ + seem to exist - since it doesn't exist,we can take over and find \ + the API method in our table. We then return a function that \ + downloads and parses what we're looking for, based on the keywords \ + passed in. + + I'll hate myself for saying this, but this is heavily inspired by \ + Ruby's "method_missing". """ def get(self, **kwargs): # Go through and replace any mustaches that are in our API url. fn = api_table[api_call] base = re.sub( '\{\{(?P[a-zA-Z_]+)\}\}', - lambda m: "%s" % kwargs.get(m.group(1), '1'), # The '1' here catches the API version. Slightly hilarious. + lambda m: "%s" % kwargs.get(m.group(1), '1'),\ + # The '1' here catches the API version. Slightly hilarious. base_url + fn['url'] ) - # Then open and load that shiiit, yo. TODO: check HTTP method and junk, handle errors/authentication + # Then open and load that shiiit, yo. + # TODO: check HTTP method and junk, handle errors/authentication if fn['method'] == 'POST': - resp, content = self.client.request(base, fn['method'], urllib.parse.urlencode(dict([k, v.encode('utf-8')] for k, v in list(kwargs.items())))) + resp, content = self.client.request(base, fn['method'], \ + urllib.parse.urlencode(dict([k, v.encode('utf-8')] \ + for k, v in list(kwargs.items())))) else: - url = base + "?" + "&".join(["%s=%s" %(key, value) for (key, value) in list(kwargs.items())]) + url = base + "?" + "&".join(["%s=%s" %(key, value) \ + for (key, value) in list(kwargs.items())]) resp, content = self.client.request(url, fn['method']) return simplejson.loads(content.decode('utf-8')) @@ -171,10 +198,12 @@ class Twython(object): resp, content = self.client.request(self.request_token_url, "GET") if resp['status'] != '200': - raise AuthError("Seems something couldn't be verified with your OAuth junk. Error: %s, Message: %s" % (resp['status'], content)) + raise AuthError("Seems something couldn't be verified with your \ + OAuth junk. Error: %s, Message: %s" % (resp['status'], content)) request_tokens = dict(urllib.parse.parse_qsl(content)) - request_tokens['auth_url'] = "%s?oauth_token=%s" % (self.authenticate_url, request_tokens['oauth_token']) + request_tokens['auth_url'] = "%s?oauth_token=%s" % \ + (self.authenticate_url, request_tokens['oauth_token']) return request_tokens def get_authorized_tokens(self): @@ -186,39 +215,52 @@ class Twython(object): resp, content = self.client.request(self.access_token_url, "GET") return dict(urllib.parse.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, - # but it's not high on the priority list at the moment. - # ------------------------------------------------------------------------------------------------------------------------ + # -------------------------------------------------------------------------- + # 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,but it's not high on \ + # the priority list at the moment. + # -------------------------------------------------------------------------- @staticmethod def constructApiURL(base_url, params): - return base_url + "?" + "&".join(["%s=%s" %(Twython.unicode2utf8(key), urllib.parse.quote_plus(Twython.unicode2utf8(value))) for (key, value) in list(params.items())]) + return base_url + "?" + "&".join(["%s=%s" %(Twython.unicode2utf8(key),\ + urllib.parse.quote_plus(Twython.unicode2utf8(value))) \ + for (key, value) in list(params.items())]) @staticmethod - 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") + 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. Parameters: url_to_shorten - URL to shorten. - shortener - In case you want to use a url shortening service other than is.gd. + shortener - In case you want to use a url shortening service \ + other than is.gd. """ try: - content = urllib.request.urlopen(shortener + "?" + urllib.parse.urlencode({query: Twython.unicode2utf8(url_to_shorten)})).read() + content = urllib.request.urlopen(shortener + "?" + \ + urllib.parse.urlencode(\ + {query: Twython.unicode2utf8(url_to_shorten)})).read() return content except HTTPError as e: - raise TwythonError("shortenURL() failed with a %s error code." % repr(e.code)) + raise TwythonError("shortenURL() failed with a %s error code." % \ + repr(e.code)) def bulkUserLookup(self, ids = None, screen_names = None, version = None): - """ bulkUserLookup(self, ids = None, screen_names = None, version = None) + """ bulkUserLookup(self, ids = None, screen_names = None, \ + version = None) - A method to do bulk user lookups against the Twitter API. Arguments (ids (numbers) / screen_names (strings)) should be flat Arrays that - contain their respective data sets. + A method to do bulk user lookups against the Twitter API. \ + Arguments (ids (numbers) / screen_names (strings)) should be flat \ + Arrays that contain their respective data sets. - Statuses for the users in question will be returned inline if they exist. Requires authentication! + Statuses for the users in question will be returned inline if \ + they exist. Requires authentication! """ apiURL = "http://api.twitter.com/1/users/lookup.json?lol=1" if ids is not None: @@ -233,7 +275,8 @@ class Twython(object): resp, content = self.client.request(apiURL, "GET") return simplejson.loads(content) except HTTPError as e: - raise TwythonError("bulkUserLookup() failed with a %s error code." % repr(e.code), e.code) + raise TwythonError("bulkUserLookup() failed with a %s error code." \ + % repr(e.code), e.code) def search(self, **kwargs): """search(search_query, **kwargs) @@ -241,16 +284,19 @@ class Twython(object): Returns tweets that match a specified query. Parameters: - See the documentation at http://dev.twitter.com/doc/get/search. Pass in the API supported arguments as named parameters. + See the documentation at http://dev.twitter.com/doc/get/search.\ + Pass in the API supported arguments as named parameters. - e.g x.search(q="jjndf") + e.g x.search(q = "python") """ - searchURL = Twython.constructApiURL("http://search.twitter.com/search.json", kwargs) + searchURL = Twython.constructApiURL(\ + "http://search.twitter.com/search.json", kwargs) try: resp, content = self.client.request(searchURL, "GET") return simplejson.loads(content) except HTTPError as e: - raise TwythonError("getSearchTimeline() failed with a %s error code." % repr(e.code), e.code) + raise TwythonError("getSearchTimeline() failed with a %s error \ + code." % repr(e.code), e.code) def searchTwitter(self, **kwargs): """use search(search_query, **kwargs) @@ -263,16 +309,20 @@ class Twython(object): Returns a generator of tweets that match a specified query. Parameters: - See the documentation at http://dev.twitter.com/doc/get/search. Pass in the API supported arguments as named parameters. + See the documentation at http://dev.twitter.com/doc/get/search.\ + Pass in the API supported arguments as named parameters. e.g x.search(search_query="python", page="2") """ - searchURL = Twython.constructApiURL("http://search.twitter.com/search.json?q=%s" % Twython.unicode2utf8(search_query), kwargs) + searchURL = Twython.constructApiURL(\ + "http://search.twitter.com/search.json?q=%s" % Twython.unicode2utf8(\ + search_query), kwargs) try: resp, content = self.client.request(searchURL, "GET") data = simplejson.loads(content) except HTTPError as e: - raise TwythonError("searchTwitterGen() failed with a %s error code." % repr(e.code), e.code) + raise TwythonError("searchTwitterGen() failed with a %s error \ + code." % repr(e.code), e.code) if not data['results']: raise StopIteration @@ -300,62 +350,94 @@ class Twython(object): 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). + Check if a specified user (id) is a member of the list in question \ + (list_id). - **Note: This method may not work for private/protected lists, unless you're authenticated and have access to those lists. + **Note: This method may not work for private/protected lists, \ + unless you're authenticated and have access to those lists. Parameters: list_id - Required. The slug of the list to check against. id - Required. The ID of the user being checked in the list. - username - User who owns the list you're checking against (username) - 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. + username - User who owns the list you're checking against \ + (username) + + 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, repr(id))) + resp, content = self.client.request("http://api.twitter.com/%d/%s/\ + %s/members/%s.json" % (version, username, list_id, repr(id))) return simplejson.loads(content) except HTTPError as e: - raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) + raise TwythonError("isListMember() failed with a %d error code." % \ + repr(e.code), e.code) 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). + Check if a specified user (id) is a subscriber of the list in + question (list_id). - **Note: This method may not work for private/protected lists, unless you're authenticated and have access to those lists. + **Note: This method may not work for private/protected lists, + unless you're authenticated and have access to those lists. Parameters: list_id - Required. The slug of the list to check against. id - Required. The ID of the user being checked in the list. - username - Required. The username of the owner of the list that you're seeing if someone is subscribed to. - 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. + username - Required. The username of the owner of the list \ + that you're seeing if someone is subscribed to. + 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 = "http://api.twitter.com/%d/%s/%s/following/%s.json" % (version, username, list_id, repr(id)) + resp, content = "http://api.twitter.com/%d/%s/%s/following/%s.json"\ + % (version, username, list_id, repr(id)) return simplejson.loads(content) except HTTPError as e: - raise TwythonError("isListMember() failed with a %d error code." % e.code, e.code) + raise TwythonError("isListMember() failed with a %d error code." % \ + repr(e.code) , e.code) + + # The following methods are apart from the other Account methods, \ + # because they rely on a whole multipart-data posting function set. - # The following methods are apart from the other Account methods, because they rely on a whole multipart-data posting function set. def updateProfileBackgroundImage(self, filename, tile="true", version = 1): """ updateProfileBackgroundImage(filename, tile="true") Updates the authenticating user's profile background image. Parameters: - image - Required. Must be a valid GIF, JPG, or PNG image of less than 800 kilobytes in size. Images with width larger than 2048 pixels will be forceably scaled down. - tile - Optional (defaults to true). If set to true the background image will be displayed tiled. The image will not be tiled otherwise. - ** Note: It's sad, but when using this method, pass the tile value as a string, e.g tile="false" - 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. + image - Required. Must be a valid GIF, JPG, or PNG image of \ + less than 800 kilobytes in size. Images with width larger than \ + 2048 pixels will be forceably scaled down. + + tile - Optional (defaults to true). If set to true the \ + background image will be displayed tiled. The image will not \ + be tiled otherwise. + + ** Note: It's sad, but when using this method, pass the tile \ + value as a string, e.g tile="false" + + 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: files = [("image", filename, open(filename, 'rb').read())] fields = [] - content_type, body = Twython.encode_multipart_formdata(fields, files) - headers = {'Content-Type': content_type, 'Content-Length': str(len(body))} - r = urllib.request.Request("http://api.twitter.com/%d/account/update_profile_background_image.json?tile=%s" % (version, tile), body, headers) + content_type, body = Twython.encode_multipart_formdata(fields, \ + files) + headers = {'Content-Type': content_type, 'Content-Length': \ + str(len(body))} + r = urllib.request.Request("http://api.twitter.com/%d/account/\ + update_profile_background_image.json?tile=%s" %\ + (version, tile), body, headers) return urllib.request.urlopen(r).read() except HTTPError as e: - raise TwythonError("updateProfileBackgroundImage() failed with a %d error code." % e.code, e.code) + raise TwythonError("updateProfileBackgroundImage() \ + failed with a %d error code." % repr(e.code), e.code) def updateProfileImage(self, filename, version = 1): """ updateProfileImage(filename) @@ -363,18 +445,27 @@ class Twython(object): Updates the authenticating user's profile image (avatar). Parameters: - 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. + 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. """ try: files = [("image", filename, open(filename, 'rb').read())] fields = [] - content_type, body = Twython.encode_multipart_formdata(fields, files) - headers = {'Content-Type': content_type, 'Content-Length': str(len(body))} - r = urllib.request.Request("http://api.twitter.com/%d/account/update_profile_image.json" % version, body, headers) + content_type, body = Twython.encode_multipart_formdata(fields, \ + files) + headers = {'Content-Type': content_type, 'Content-Length': \ + str(len(body))} + r = urllib.request.Request("http://api.twitter.com/%d/account/\ + update_profile_image.json" % version, body, headers) return urllib.request.urlopen(r).read() except HTTPError as e: - raise TwythonError("updateProfileImage() failed with a %d error code." % e.code, e.code) + raise TwythonError("updateProfileImage() failed with a %d error \ + code." % repr(e.code), e.code) @staticmethod def encode_multipart_formdata(fields, files): @@ -388,8 +479,10 @@ class Twython(object): L.append(value) for (key, filename, value) in files: L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) - L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0] or 'application/octet-stream') + L.append('Content-Disposition: form-data; name="%s"; filename="%s"'\ + % (key, filename)) + L.append('Content-Type: %s' % mimetypes.guess_type(filename)[0] or \ + 'application/octet-stream') L.append('') L.append(value) L.append('--' + BOUNDARY + '--')