v1.5.0 release

- requests is now the default url/http library, thanks to Mike Helmick
- Initial pass at a Streaming API is now included (Twython.stream()), due to how easy
    requests makes it. Would actually be sad if we *didn't* have this... thanks, Kenneth. >_>;
- Return of shortenURL, for people who may have relied on it before.
- Deleted streaming handler that existed before but never got implemented fully.
- Exceptions now prefixed with Twython, but brought back originals with a more verbose error directing
    people to new ones, deprecate fully in future.
- Twython3k now has an OAuth fix for callback_urls, though it still relies on httplib2. Thanks @jbouvier!
- Added a list of contributors to the README files, something which I should have done long ago. Thank you all.
This commit is contained in:
Ryan McGrath 2012-03-21 19:21:34 +01:00
parent 1a6852de54
commit 9deced8f8b
6 changed files with 266 additions and 152 deletions

View file

@ -88,4 +88,30 @@ My hope is that Twython is so simple that you'd never *have* to ask any question
you feel the need to contact me for this (or other) reasons, you can hit me up
at ryan@venodesigns.net.
You can also follow me on Twitter - **[@ryanmcgrath](http://twitter.com/ryanmcgrath)**.
Twython is released under an MIT License - see the LICENSE file for more information.
Special Thanks to...
-----------------------------------------------------------------------------------------------------
This is a list of all those who have contributed code to Twython in some way, shape, or form. I think it's
exhaustive, but I could be wrong - if you think your name should be here and it's not, please contact
me and let me know (or just issue a pull request on GitHub, and leave a note about it so I can just accept it ;)).
- **[Mike Helmick (michaelhelmick)](https://github.com/michaelhelmick)**, multiple fixes and proper `requests` integration.
- **[kracekumar](https://github.com/kracekumar)**, early `requests` work and various fixes.
- **[Erik Scheffers (eriks5)](https://github.com/eriks5)**, various fixes regarding OAuth callback URLs.
- **[Jordan Bouvier (jbouvier)](https://github.com/jbouvier)**, various fixes regarding OAuth callback URLs.
- **[Dick Brouwer (dikbrouwer)](https://github.com/dikbrouwer)**, fixes for OAuth Verifier in `get_authorized_tokens`.
- **[hades](https://github.com/hades)**, Fixes to various initial OAuth issues and updates to `Twython3k` to stay current.
- **[Alex Sutton (alexdsutton)](https://github.com/alexsdutton/twython/)**, fix for parameter substitution regular expression (catch underscores!).
- **[Levgen Pyvovarov (bsn)](https://github.com/bsn)**, Various argument fixes, cyrillic text support.
- **[Mark Liu (mliu7)](https://github.com/mliu7)**, Missing parameter fix for `addListMember`.
- **[Randall Degges (rdegges)](https://github.com/rdegges)**, PEP-8 fixes, MANIFEST.in, installer fixes.
- **[Idris Mokhtarzada (idris)](https://github.com/idris)**, Fixes for various example code pieces.
- **[Jonathan Elsas (jelsas)](https://github.com/jelsas)**, Fix for original Streaming API stub causing import errors.
- **[LuqueDaniel](https://github.com/LuqueDaniel)**, Extended example code where necessary.
- **[Mesar Hameed (mhameed)](https://github.com/mhameed)**, Commit to swap `__getattr__` trick for a more debuggable solution.
- **[Remy DeCausemaker (decause)](https://github.com/decause)**, PEP-8 contributions.
- **[mckellister](https://github.com/mckellister)**, Fixes to `Exception`s raised by Twython (Rate Limits, etc).
- **[tatz_tsuchiya](http://d.hatena.ne.jp/tatz_tsuchiya/20120115/1326623451), Fix for `lambda` scoping in key injection phase.

View file

@ -16,7 +16,7 @@ for those types of use cases. Twython cannot help you with that or fix the annoy
If you need OAuth, though, Twython now supports it, and ships with a skeleton Django application to get you started.
Enjoy!
Requirements
Requirements (2.7 and below; for 3k, read section further down)
-----------------------------------------------------------------------------------------------------
Twython (for versions of Python before 2.6) requires a library called
"simplejson". Depending on your flavor of package manager, you can do the following...
@ -35,20 +35,23 @@ Installing Twython is fairly easy. You can...
...or, you can clone the repo and install it the old fashioned way.
git clone git://github.com/ryanmcgrath/twython.git
cd twython
sudo python setup.py install
Example Use
-----------------------------------------------------------------------------------------------------
from twython import Twython
``` python
from twython import Twython
twitter = Twython()
results = twitter.searchTwitter(q="bert")
twitter = Twython()
results = twitter.search(q = "bert")
# More function definitions can be found by reading over twython/twitter_endpoints.py, as well
# as skimming the source file. Both are kept human-readable, and are pretty well documented or
# very self documenting.
# More function definitions can be found by reading over twython/twitter_endpoints.py, as well
# as skimming the source file. Both are kept human-readable, and are pretty well documented or
# very self documenting.
```
A note about the development of Twython (specifically, 1.3)
----------------------------------------------------------------------------------------------------
@ -65,17 +68,19 @@ Arguments to functions are now exact keyword matches for the Twitter API documen
whatever query parameter arguments you read on Twitter's documentation (http://dev.twitter.com/doc) gets mapped
as a named argument to any Twitter function.
For example: the search API looks for arguments under the name "q", so you pass q="query_here" to searchTwitter().
For example: the search API looks for arguments under the name "q", so you pass q="query_here" to search().
Doing this allows us to be incredibly flexible in querying the Twitter API, so changes to the API aren't held up
from you using them by this library.
Twython 3k
-----------------------------------------------------------------------------------------------------
There's an experimental version of Twython that's made for Python 3k. This is currently not guaranteed
to work (especially with regards to OAuth), but it's provided so that others can grab it and hack on it.
There's an experimental version of Twython that's made for Python 3k. This is currently not guaranteed to
work in all situations, but it's provided so that others can grab it and hack on it.
If you choose to try it out, be aware of this.
**OAuth is now working thanks to updates from [Hades](https://github.com/hades). You'll need to grab
his [Python 3 branch for python-oauth2](https://github.com/hades/python-oauth2/tree/python3) to have it work, though.**
Questions, Comments, etc?
-----------------------------------------------------------------------------------------------------
@ -83,4 +88,30 @@ My hope is that Twython is so simple that you'd never *have* to ask any question
you feel the need to contact me for this (or other) reasons, you can hit me up
at ryan@venodesigns.net.
You can also follow me on Twitter - **[@ryanmcgrath](http://twitter.com/ryanmcgrath)**.
Twython is released under an MIT License - see the LICENSE file for more information.
Special Thanks to...
-----------------------------------------------------------------------------------------------------
This is a list of all those who have contributed code to Twython in some way, shape, or form. I think it's
exhaustive, but I could be wrong - if you think your name should be here and it's not, please contact
me and let me know (or just issue a pull request on GitHub, and leave a note about it so I can just accept it ;)).
- **[Mike Helmick (michaelhelmick)](https://github.com/michaelhelmick)**, multiple fixes and proper `requests` integration.
- **[kracekumar](https://github.com/kracekumar)**, early `requests` work and various fixes.
- **[Erik Scheffers (eriks5)](https://github.com/eriks5)**, various fixes regarding OAuth callback URLs.
- **[Jordan Bouvier (jbouvier)](https://github.com/jbouvier)**, various fixes regarding OAuth callback URLs.
- **[Dick Brouwer (dikbrouwer)](https://github.com/dikbrouwer)**, fixes for OAuth Verifier in `get_authorized_tokens`.
- **[hades](https://github.com/hades)**, Fixes to various initial OAuth issues and updates to `Twython3k` to stay current.
- **[Alex Sutton (alexdsutton)](https://github.com/alexsdutton/twython/)**, fix for parameter substitution regular expression (catch underscores!).
- **[Levgen Pyvovarov (bsn)](https://github.com/bsn)**, Various argument fixes, cyrillic text support.
- **[Mark Liu (mliu7)](https://github.com/mliu7)**, Missing parameter fix for `addListMember`.
- **[Randall Degges (rdegges)](https://github.com/rdegges)**, PEP-8 fixes, MANIFEST.in, installer fixes.
- **[Idris Mokhtarzada (idris)](https://github.com/idris)**, Fixes for various example code pieces.
- **[Jonathan Elsas (jelsas)](https://github.com/jelsas)**, Fix for original Streaming API stub causing import errors.
- **[LuqueDaniel](https://github.com/LuqueDaniel)**, Extended example code where necessary.
- **[Mesar Hameed (mhameed)](https://github.com/mhameed)**, Commit to swap `__getattr__` trick for a more debuggable solution.
- **[Remy DeCausemaker (decause)](https://github.com/decause)**, PEP-8 contributions.
- **[mckellister](https://github.com/mckellister)**, Fixes to `Exception`s raised by Twython (Rate Limits, etc).
- **[tatz_tsuchiya](http://d.hatena.ne.jp/tatz_tsuchiya/20120115/1326623451), Fix for `lambda` scoping in key injection phase.

View file

@ -4,7 +4,7 @@ from setuptools import setup
from setuptools import find_packages
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
__version__ = '1.4.6'
__version__ = '1.5.0'
setup(
# Basic package information.

View file

@ -1,61 +0,0 @@
#!/usr/bin/python
"""
TwythonStreamer is an implementation of the Streaming API for Twython.
Pretty self explanatory by reading the code below. It's worth noting
that the end user should, ideally, never import this library, but rather
this is exposed via a linking method in Twython's core.
Questions, comments? ryan@venodesigns.net
"""
__author__ = "Ryan McGrath <ryan@venodesigns.net>"
__version__ = "1.0.0"
import urllib
import urllib2
import urlparse
import httplib
import httplib2
import re
from urllib2 import HTTPError
# There are some special setups (like, oh, a Django application) where
# simplejson exists behind the scenes anyway. Past Python 2.6, this should
# never really cause any problems to begin with.
try:
# Python 2.6 and up
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)
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.
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/")
class TwythonStreamingError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return str(self.msg)
feeds = {
"firehose": "http://stream.twitter.com/firehose.json",
"gardenhose": "http://stream.twitter.com/gardenhose.json",
"spritzer": "http://stream.twitter.com/spritzer.json",
"birddog": "http://stream.twitter.com/birddog.json",
"shadow": "http://stream.twitter.com/shadow.json",
"follow": "http://stream.twitter.com/follow.json",
"track": "http://stream.twitter.com/track.json",
}
class Stream(object):
def __init__(self, username = None, password = None, feed = "spritzer", user_agent = "Twython Streaming"):
pass

View file

@ -9,7 +9,7 @@
"""
__author__ = "Ryan McGrath <ryan@venodesigns.net>"
__version__ = "1.4.6"
__version__ = "1.5.0"
import urllib
import re
@ -95,6 +95,20 @@ class TwythonAPILimit(TwythonError):
def __str__(self):
return repr(self.msg)
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.
DEPRECATED, import and catch TwythonAPILimit instead.
"""
def __init__(self, msg):
self.msg = '%s\n Notice: APILimit is deprecated and soon to be removed, catch on TwythonAPILimit instead!' % msg
def __str__(self):
return repr(self.msg)
class TwythonRateLimitError(TwythonError):
"""
@ -121,6 +135,18 @@ class TwythonAuthError(TwythonError):
return repr(self.msg)
class AuthError(TwythonError):
"""
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 = '%s\n Notice: AuthError is deprecated and soon to be removed, catch on TwythonAuthError instead!' % msg
def __str__(self):
return repr(self.msg)
class Twython(object):
def __init__(self, twitter_token=None, twitter_secret=None, oauth_token=None, oauth_token_secret=None, \
headers=None, callback_url=None):
@ -140,7 +166,6 @@ class Twython(object):
** Note: versioning is not currently used by search.twitter functions;
when Twitter moves their junk, it'll be supported.
"""
OAuthHook.consumer_key = twitter_token
OAuthHook.consumer_secret = twitter_secret
@ -159,7 +184,7 @@ class Twython(object):
# 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.4.6'}
self.headers = {'User-agent': 'Twython Python Twitter Library v' + __version__}
self.client = None
@ -186,8 +211,7 @@ class Twython(object):
fn = api_table[api_call]
base = re.sub(
'\{\{(?P<m>[a-zA-Z_]+)\}\}',
# The '1' here catches the API version. Slightly
# hilarious.
# The '1' here catches the API version. Slightly hilarious.
lambda m: "%s" % kwargs.get(m.group(1), '1'),
base_url + fn['url']
)
@ -256,7 +280,6 @@ class Twython(object):
Returns authorized tokens after they go through the auth_url phase.
"""
response = self.client.get(self.access_token_url)
authorized_tokens = dict(parse_qsl(response.content))
if not authorized_tokens:
@ -270,6 +293,28 @@ class Twython(object):
# but it's not high on the priority list at the moment.
# ------------------------------------------------------------------------------------------------------------------------
@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")
Shortens url specified by url_to_shorten.
Note: Twitter automatically shortens all URLs behind their own custom t.co shortener now,
but we keep this here for anyone who was previously using it for alternative purposes. ;)
Parameters:
url_to_shorten - URL to shorten.
shortener = In case you want to use a url shortening service other than is.gd.
"""
request = requests.get('http://is.gd/api.php' , params = {
'query': url_to_shorten
})
if r.status_code in [301, 201, 200]:
return request.text
else:
raise TwythonError('shortenURL() failed with a %s error code.' % r.status_code)
@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()])
@ -418,7 +463,9 @@ 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)
@ -429,7 +476,9 @@ 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):
@ -441,7 +490,9 @@ 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=None):
params = params or {}
@ -459,7 +510,7 @@ class Twython(object):
header.. that MIGHT be why it's not working..
I haven't debugged enough.
- Mike Helmick
- Mike Helmick
***
self.oauth_hook.header_auth = True
@ -532,6 +583,48 @@ class Twython(object):
raise TwythonError("getProfileImageUrl() failed with a %d error code." % response.status_code, response.status_code)
@staticmethod
def stream(data, callback):
"""
A Streaming API endpoint, because requests (by the lovely Kenneth Reitz) makes this not
stupidly annoying to implement. In reality, Twython does absolutely *nothing special* here,
but people new to programming expect this type of function to exist for this library, so we
provide it for convenience.
Seriously, this is nothing special. :)
For the basic stream you're probably accessing, you'll want to pass the following as data dictionary
keys. If you need to use OAuth (newer streams), passing secrets/etc as keys SHOULD work...
username - Required. User name, self explanatory.
password - Required. The Streaming API doesn't use OAuth, so we do this the old school way. It's all
done over SSL (https://), so you're not left totally vulnerable.
endpoint - Optional. Override the endpoint you're using with the Twitter Streaming API. This is defaulted to the one
that everyone has access to, but if Twitter <3's you feel free to set this to your wildest desires.
Parameters:
data - Required. Dictionary of attributes to attach to the request (see: params https://dev.twitter.com/docs/streaming-api/methods)
callback - Required. Callback function to be fired when tweets come in (this is an event-based-ish API).
"""
endpoint = 'https://stream.twitter.com/1/statuses/filter.json'
if 'endpoint' in data:
endpoint = data.pop('endpoint')
needs_basic_auth = False
if 'username' in data:
needs_basic_auth = True
username = data.pop('username')
password = data.pop('password')
if needs_basic_auth:
stream = requests.post(endpoint, data = data, auth = (username, password))
else:
stream = requests.post(endpoint, data = data)
for line in stream.iter_lines():
if line:
callback(json.loads(line))
@staticmethod
def unicode2utf8(text):
try:

View file

@ -9,7 +9,7 @@
"""
__author__ = "Ryan McGrath <ryan@venodesigns.net>"
__version__ = "1.4.6"
__version__ = "1.4.7"
import cgi
import urllib.request, urllib.parse, urllib.error
@ -37,16 +37,8 @@ try:
# Python 2.6 and up
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)
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.
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/")
# Seriously wtf is wrong with you if you get this Exception.
raise Exception("Twython3k requires a json library 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
@ -81,7 +73,7 @@ class TwythonError(AttributeError):
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
@ -94,7 +86,22 @@ class APILimit(TwythonError):
return repr(self.msg)
class AuthError(TwythonError):
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.
DEPRECATED, you should be importing TwythonAPILimit instead. :)
"""
def __init__(self, msg):
self.msg = '%s\n Notice: APILimit is deprecated and soon to be removed, catch TwythonAPILimit instead!' % msg
def __str__(self):
return repr(self.msg)
class TwythonAuthError(TwythonError):
"""
Raised when you try to access a protected resource and it fails due to some issue with
your authentication.
@ -105,6 +112,19 @@ class AuthError(TwythonError):
def __str__(self):
return repr(self.msg)
class AuthError(TwythonError):
"""
Raised when you try to access a protected resource and it fails due to some issue with
your authentication.
DEPRECATED, you should be importing TwythonAuthError instead.
"""
def __init__(self, msg):
self.msg = '%s\n Notice: AuthLimit is deprecated and soon to be removed, catch TwythonAPILimit instead!' % msg
def __str__(self):
return repr(self.msg)
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={}):
@ -189,10 +209,16 @@ class Twython(object):
callback_url = self.callback_url or 'oob'
request_args = {}
method = 'GET'
if OAUTH_LIB_SUPPORTS_CALLBACK:
request_args['callback_url'] = callback_url
else:
# This is a hack for versions of oauth that don't support the callback URL. This is also
# done differently than the Python2 version of Twython, which uses Requests internally (as opposed to httplib2).
request_args['body'] = urllib.urlencode({'oauth_callback': callback_url})
method = 'POST'
resp, content = self.client.request(self.request_token_url, "GET", **request_args)
resp, content = self.client.request(self.request_token_url, method, **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))
@ -213,7 +239,6 @@ class Twython(object):
'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