Merge pull request #183 from ryanmcgrath/python3-compat
Python 3 Compatibility
This commit is contained in:
commit
f3d4a0d641
30 changed files with 406 additions and 1123 deletions
39
.gitignore
vendored
39
.gitignore
vendored
|
|
@ -1,5 +1,36 @@
|
|||
*.pyc
|
||||
build
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
twython.egg-info
|
||||
*.swp
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
__pycache__
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
|
@ -13,7 +13,7 @@ Development Lead
|
|||
Patches and Suggestions
|
||||
````````````````````````
|
||||
|
||||
- `Mike Helmick <https://github.com/michaelhelmick>`_, multiple fixes and proper ``requests`` integration. Too much to list here.
|
||||
- `Mike Helmick <https://github.com/michaelhelmick>`_, multiple fixes and proper ``requests`` integration, Python 3 compatibility, too much to list here.
|
||||
- `kracekumar <https://github.com/kracekumar>`_, early ``requests`` work and various fixes.
|
||||
- `Erik Scheffers <https://github.com/eriks5>`_, various fixes regarding OAuth callback URLs.
|
||||
- `Jordan Bouvier <https://github.com/jbouvier>`_, various fixes regarding OAuth callback URLs.
|
||||
|
|
@ -38,3 +38,4 @@ Patches and Suggestions
|
|||
- `Virendra Rajput <https://github.com/bkvirendra>`_, Fixed unicode (json) encoding in twython.py 2.7.2.
|
||||
- `Paul Solbach <https://github.com/hansenrum>`_, fixed requirement for oauth_verifier
|
||||
- `Greg Nofi <https://github.com/nofeet>`_, fixed using built-in Exception attributes for storing & retrieving error message
|
||||
- `Jonathan Vanasco <https://github.com/jvanasco>`_, Debugging support, error_code tracking, Twitter error API tracking, other fixes
|
||||
|
|
|
|||
63
HISTORY.rst
Normal file
63
HISTORY.rst
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
History
|
||||
-------
|
||||
|
||||
2.8.0 (2013-04-29)
|
||||
++++++++++++++++++
|
||||
|
||||
- Added a ``HISTORY.rst`` to start tracking history of changes
|
||||
- Updated ``twitter_endpoints.py`` to ``endpoints.py`` for cleanliness
|
||||
- Removed twython3k directory, no longer needed
|
||||
- Added ``compat.py`` for compatability with Python 2.6 and greater
|
||||
- Added some ascii art, moved description of Twython and ``__author__`` to ``__init__.py``
|
||||
- Added ``version.py`` to store the current Twython version, instead of repeating it twice -- it also had to go into it's own file because of dependencies of ``requests`` and ``requests-oauthlib``, install would fail because those libraries weren't installed yet (on fresh install of Twython)
|
||||
- Removed ``find_packages()`` from ``setup.py``, only one package -- we can
|
||||
just define it
|
||||
- added quick publish method for Ryan and I: ``python setup.py publish`` is faster to type and easier to remember than ``python setup.py sdist upload``
|
||||
- Removed ``base_url`` from ``endpoints.py`` because we're just repeating it in
|
||||
``Twython.__init__``
|
||||
- ``Twython.get_authentication_tokens()`` now takes ``callback_url`` argument rather than passing the ``callback_url`` through ``Twython.__init__``, ``callback_url`` is only used in the ``get_authentication_tokens`` method and nowhere else (kept in init though for backwards compatability)
|
||||
- Updated README to better reflect current Twython codebase
|
||||
- Added ``warnings.simplefilter('default')`` line in ``twython.py`` for Python 2.7 and greater to display Deprecation Warnings in console
|
||||
- Added Deprecation Warnings for usage of ``twitter_token``, ``twitter_secret`` and ``callback_url`` in ``Twython.__init__``
|
||||
- Headers now always include the User-Agent as Twython vXX unless User-Agent is overwritten
|
||||
- Removed senseless TwythonError thrown if method is not GET or POST, who cares -- if the user passes something other than GET or POST just let Twitter return the error that they messed up
|
||||
- Removed conversion to unicode of (int, bool) params passed to a requests. ``requests`` isn't greedy about variables that can't be converted to unicode anymore
|
||||
- Removed `bulkUserLookup` (please use `lookupUser` instead), removed `getProfileImageUrl` (will be completely removed from Twitter API on May 7th, 2013)
|
||||
- Updated shortenUrl to actually work for those using it, but it is being deprecated since `requests` makes it easy for developers to implement their own url shortening in their app (see https://github.com/ryanmcgrath/twython/issues/184)
|
||||
- Twython Deprecation Warnings will now be seen in shell when using Python 2.7 and greater
|
||||
- Twython now takes ``ssl_verify`` parameter, defaults True. Set False if you're having development server issues
|
||||
- Removed internal ``_media_update`` function, we could have always just used ``self.post``
|
||||
|
||||
2.7.3 (2013-04-12)
|
||||
++++++++++++++++++
|
||||
|
||||
- Fixed issue where Twython Exceptions were not being logged correctly
|
||||
|
||||
2.7.2 (2013-04-08)
|
||||
++++++++++++++++++
|
||||
|
||||
- Fixed ``AttributeError`` when trying to decode the JSON response via ``Response.json()``
|
||||
|
||||
2.7.1 (2013-04-08)
|
||||
++++++++++++++++++
|
||||
|
||||
- Removed ``simplejson`` dependency
|
||||
- Fixed ``destroyDirectMessage``, ``createBlock``, ``destroyBlock`` endpoints in ``twitter_endpoints.py``
|
||||
- Added ``getProfileBannerSizes`` method to ``twitter_endpoints.py``
|
||||
- Made oauth_verifier argument required in ``get_authorized_tokens``
|
||||
- Update ``updateProfileBannerImage`` to use v1.1 endpoint
|
||||
|
||||
2.7.0 (2013-04-04)
|
||||
++++++++++++++++++
|
||||
|
||||
- New ``showOwnedLists`` method
|
||||
|
||||
2.7.0 (2013-03-31)
|
||||
++++++++++++++++++
|
||||
|
||||
- Added missing slash to ``getMentionsTimeline`` in ``twitter_endpoints.py``
|
||||
|
||||
2.6.0 (2013-03-29)
|
||||
++++++++++++++++++
|
||||
|
||||
- Updated ``twitter_endpoints.py`` to better reflect order of API endpoints on the Twitter API v1.1 docs site
|
||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2009 - 2010 Ryan McGrath
|
||||
Copyright (c) 2013 Ryan McGrath
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
include LICENSE README.md README.rst
|
||||
include LICENSE README.md README.rst HISTORY.rst
|
||||
recursive-include examples *
|
||||
recursive-exclude examples *.pyc
|
||||
|
|
|
|||
73
README.md
73
README.md
|
|
@ -10,12 +10,13 @@ Features
|
|||
- User information
|
||||
- Twitter lists
|
||||
- Timelines
|
||||
- User avatar URL
|
||||
- Direct Messages
|
||||
- and anything found in [the docs](https://dev.twitter.com/docs/api/1.1)
|
||||
* Image Uploading!
|
||||
- **Update user status with an image**
|
||||
- Change user avatar
|
||||
- Change user background image
|
||||
- Change user banner image
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
|
@ -31,16 +32,14 @@ Installation
|
|||
Usage
|
||||
-----
|
||||
|
||||
###### Authorization URL
|
||||
##### Authorization URL
|
||||
|
||||
```python
|
||||
from twython import Twython
|
||||
|
||||
t = Twython(app_key=app_key,
|
||||
app_secret=app_secret,
|
||||
callback_url='http://google.com/')
|
||||
t = Twython(app_key, app_secret)
|
||||
|
||||
auth_props = t.get_authentication_tokens()
|
||||
auth_props = t.get_authentication_tokens(callback_url='http://google.com')
|
||||
|
||||
oauth_token = auth_props['oauth_token']
|
||||
oauth_token_secret = auth_props['oauth_token_secret']
|
||||
|
|
@ -50,47 +49,60 @@ print 'Connect to Twitter via: %s' % auth_props['auth_url']
|
|||
|
||||
Be sure you have a URL set up to handle the callback after the user has allowed your app to access their data, the callback can be used for storing their final OAuth Token and OAuth Token Secret in a database for use at a later date.
|
||||
|
||||
###### Handling the callback
|
||||
##### Handling the callback
|
||||
|
||||
```python
|
||||
from twython import Twython
|
||||
|
||||
'''
|
||||
oauth_token and oauth_token_secret come from the previous step
|
||||
if needed, store those in a session variable or something. oauth_verifier from the previous call is now required to pass to get_authorized_tokens
|
||||
'''
|
||||
# oauth_token_secret comes from the previous step
|
||||
# if needed, store that in a session variable or something.
|
||||
# oauth_verifier and oauth_token from the previous call is now REQUIRED # to pass to get_authorized_tokens
|
||||
|
||||
t = Twython(app_key=app_key,
|
||||
app_secret=app_secret,
|
||||
oauth_token=oauth_token,
|
||||
oauth_token_secret=oauth_token_secret)
|
||||
# In Django, to get the oauth_verifier and oauth_token from the callback
|
||||
# url querystring, you might do something like this:
|
||||
# oauth_token = request.GET.get('oauth_token')
|
||||
# oauth_verifier = request.GET.get('oauth_verifier')
|
||||
|
||||
t = Twython(app_key, app_secret,
|
||||
oauth_token, oauth_token_secret)
|
||||
|
||||
auth_tokens = t.get_authorized_tokens(oauth_verifier)
|
||||
print auth_tokens
|
||||
```
|
||||
|
||||
*Function definitions (i.e. getHomeTimeline()) can be found by reading over twython/twitter_endpoints.py*
|
||||
*Function definitions (i.e. getHomeTimeline()) can be found by reading over twython/endpoints.py*
|
||||
|
||||
###### Getting a user home timeline
|
||||
##### Getting a user home timeline
|
||||
|
||||
```python
|
||||
from twython import Twython
|
||||
|
||||
'''
|
||||
oauth_token and oauth_token_secret are the final tokens produced
|
||||
from the `Handling the callback` step
|
||||
'''
|
||||
# oauth_token and oauth_token_secret are the final tokens produced
|
||||
# from the 'Handling the callback' step
|
||||
|
||||
t = Twython(app_key=app_key,
|
||||
app_secret=app_secret,
|
||||
oauth_token=oauth_token,
|
||||
oauth_token_secret=oauth_token_secret)
|
||||
t = Twython(app_key, app_secret,
|
||||
oauth_token, oauth_token_secret)
|
||||
|
||||
# Returns an dict of the user home timeline
|
||||
print t.getHomeTimeline()
|
||||
```
|
||||
|
||||
###### Streaming API
|
||||
##### Catching exceptions
|
||||
> Twython offers three Exceptions currently: TwythonError, TwythonAuthError and TwythonRateLimitError
|
||||
|
||||
```python
|
||||
from twython import Twython, TwythonAuthError
|
||||
|
||||
t = Twython(MY_WRONG_APP_KEY, MY_WRONG_APP_SECRET,
|
||||
BAD_OAUTH_TOKEN, BAD_OAUTH_TOKEN_SECRET)
|
||||
|
||||
try:
|
||||
t.verifyCredentials()
|
||||
except TwythonAuthError as e:
|
||||
print e
|
||||
```
|
||||
|
||||
##### Streaming API
|
||||
*Usage is as follows; it's designed to be open-ended enough that you can adapt it to higher-level (read: Twitter must give you access)
|
||||
streams.*
|
||||
|
||||
|
|
@ -136,12 +148,7 @@ 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 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.**
|
||||
Full compatiabilty with Python 3 is now available seamlessly in the main Twython package. The Twython 3k package has been removed as of Twython 2.8.0
|
||||
|
||||
Questions, Comments, etc?
|
||||
-------------------------
|
||||
|
|
@ -150,8 +157,6 @@ 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.
|
||||
|
||||
Want to help?
|
||||
-------------
|
||||
Twython is useful, but ultimately only as useful as the people using it (say that ten times fast!). If you'd like to help, write example code, contribute patches, document things on the wiki, tweet about it. Your help is always appreciated!
|
||||
|
|
|
|||
71
README.rst
71
README.rst
|
|
@ -9,12 +9,13 @@ Features
|
|||
- User information
|
||||
- Twitter lists
|
||||
- Timelines
|
||||
- User avatar URL
|
||||
- Direct Messages
|
||||
- and anything found in `the docs <https://dev.twitter.com/docs/api/1.1>`_
|
||||
* Image Uploading!
|
||||
- **Update user status with an image**
|
||||
- Change user avatar
|
||||
- Change user background image
|
||||
- Change user banner image
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
|
@ -37,13 +38,12 @@ Usage
|
|||
Authorization URL
|
||||
~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
from twython import Twython
|
||||
|
||||
t = Twython(app_key=app_key,
|
||||
app_secret=app_secret,
|
||||
callback_url='http://google.com/')
|
||||
t = Twython(app_key, app_secret)
|
||||
|
||||
auth_props = t.get_authentication_tokens()
|
||||
auth_props = t.get_authentication_tokens(callback_url='http://google.com')
|
||||
|
||||
oauth_token = auth_props['oauth_token']
|
||||
oauth_token_secret = auth_props['oauth_token_secret']
|
||||
|
|
@ -56,41 +56,59 @@ Handling the callback
|
|||
~~~~~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
'''
|
||||
oauth_token and oauth_token_secret come from the previous step
|
||||
if needed, store those in a session variable or something. oauth_verifier from the previous call is now required to pass to get_authorized_tokens
|
||||
'''
|
||||
from twython import Twython
|
||||
|
||||
t = Twython(app_key=app_key,
|
||||
app_secret=app_secret,
|
||||
oauth_token=oauth_token,
|
||||
oauth_token_secret=oauth_token_secret)
|
||||
# oauth_token_secret comes from the previous step
|
||||
# if needed, store that in a session variable or something.
|
||||
# oauth_verifier and oauth_token from the previous call is now REQUIRED # to pass to get_authorized_tokens
|
||||
|
||||
# In Django, to get the oauth_verifier and oauth_token from the callback
|
||||
# url querystring, you might do something like this:
|
||||
# oauth_token = request.GET.get('oauth_token')
|
||||
# oauth_verifier = request.GET.get('oauth_verifier')
|
||||
|
||||
t = Twython(app_key, app_secret,
|
||||
oauth_token, oauth_token_secret)
|
||||
|
||||
auth_tokens = t.get_authorized_tokens(oauth_verifier)
|
||||
print auth_tokens
|
||||
|
||||
*Function definitions (i.e. getHomeTimeline()) can be found by reading over twython/twitter_endpoints.py*
|
||||
*Function definitions (i.e. getHomeTimeline()) can be found by reading over twython/endpoints.py*
|
||||
|
||||
Getting a user home timeline
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
'''
|
||||
oauth_token and oauth_token_secret are the final tokens produced
|
||||
from the `Handling the callback` step
|
||||
'''
|
||||
from twython import Twython
|
||||
|
||||
t = Twython(app_key=app_key,
|
||||
app_secret=app_secret,
|
||||
oauth_token=oauth_token,
|
||||
oauth_token_secret=oauth_token_secret)
|
||||
# oauth_token and oauth_token_secret are the final tokens produced
|
||||
# from the 'Handling the callback' step
|
||||
|
||||
t = Twython(app_key, app_secret,
|
||||
oauth_token, oauth_token_secret)
|
||||
|
||||
# Returns an dict of the user home timeline
|
||||
print t.getHomeTimeline()
|
||||
|
||||
|
||||
Catching exceptions
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Twython offers three Exceptions currently: ``TwythonError``, ``TwythonAuthError`` and ``TwythonRateLimitError``
|
||||
|
||||
::
|
||||
|
||||
from twython import Twython, TwythonAuthError
|
||||
|
||||
t = Twython(MY_WRONG_APP_KEY, MY_WRONG_APP_SECRET,
|
||||
BAD_OAUTH_TOKEN, BAD_OAUTH_TOKEN_SECRET)
|
||||
|
||||
try:
|
||||
t.verifyCredentials()
|
||||
except TwythonAuthError as e:
|
||||
print e
|
||||
|
||||
|
||||
Streaming API
|
||||
~~~~~~~~~~~~~
|
||||
*Usage is as follows; it's designed to be open-ended enough that you can adapt it to higher-level (read: Twitter must give you access)
|
||||
|
|
@ -143,12 +161,7 @@ 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 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.**
|
||||
Full compatiabilty with Python 3 is now available seamlessly in the main Twython package. The Twython 3k package has been removed as of Twython 2.8.0
|
||||
|
||||
Questions, Comments, etc?
|
||||
-------------------------
|
||||
|
|
@ -156,8 +169,6 @@ My hope is that Twython is so simple that you'd never *have* to ask any question
|
|||
|
||||
You can also follow me on Twitter - `@ryanmcgrath <https://twitter.com/ryanmcgrath>`_
|
||||
|
||||
*Twython is released under an MIT License - see the LICENSE file for more information.*
|
||||
|
||||
Want to help?
|
||||
-------------
|
||||
Twython is useful, but ultimately only as useful as the people using it (say that ten times fast!). If you'd like to help, write example code, contribute patches, document things on the wiki, tweet about it. Your help is always appreciated!
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
""" Instantiate Twython with no Authentication """
|
||||
twitter = Twython()
|
||||
trends = twitter.getCurrentTrends()
|
||||
|
||||
print trends
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
""" Instantiate Twython with no Authentication """
|
||||
twitter = Twython()
|
||||
trends = twitter.getDailyTrends()
|
||||
|
||||
print trends
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
# We won't authenticate for this, but sometimes it's necessary
|
||||
twitter = Twython()
|
||||
user_timeline = twitter.getUserTimeline(screen_name="ryanmcgrath")
|
||||
|
||||
print user_timeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
""" Instantiate Twython with no Authentication """
|
||||
twitter = Twython()
|
||||
search_results = twitter.search(q="WebsDotCom", rpp="50")
|
||||
|
||||
for tweet in search_results["results"]:
|
||||
print "Tweet from @%s Date: %s" % (tweet['from_user'].encode('utf-8'),tweet['created_at'])
|
||||
print tweet['text'].encode('utf-8'),"\n"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
"""
|
||||
You'll need to go through the OAuth ritual to be able to successfully
|
||||
use this function. See the example oauth django application included in
|
||||
this package for more information.
|
||||
"""
|
||||
twitter = Twython()
|
||||
twitter.updateProfileImage("myImage.png")
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
"""
|
||||
Note: for any method that'll require you to be authenticated (updating
|
||||
things, etc)
|
||||
you'll need to go through the OAuth authentication ritual. See the example
|
||||
Django application that's included with this package for more information.
|
||||
"""
|
||||
twitter = Twython()
|
||||
|
||||
# OAuth ritual...
|
||||
|
||||
|
||||
twitter.updateStatus(status="See how easy this was?")
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
from twython import Twython
|
||||
|
||||
""" Instantiate Twython with no Authentication """
|
||||
twitter = Twython()
|
||||
trends = twitter.getWeeklyTrends()
|
||||
|
||||
print trends
|
||||
10
examples/get_user_timeline.py
Normal file
10
examples/get_user_timeline.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from twython import Twython, TwythonError
|
||||
|
||||
# Requires Authentication as of Twitter API v1.1
|
||||
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
|
||||
try:
|
||||
user_timeline = twitter.getUserTimeline(screen_name='ryanmcgrath')
|
||||
except TwythonError as e:
|
||||
print e
|
||||
|
||||
print user_timeline
|
||||
12
examples/search_results.py
Normal file
12
examples/search_results.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from twython import Twython, TwythonError
|
||||
|
||||
# Requires Authentication as of Twitter API v1.1
|
||||
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
|
||||
try:
|
||||
search_results = twitter.search(q="WebsDotCom", rpp="50")
|
||||
except TwythonError as e:
|
||||
print e
|
||||
|
||||
for tweet in search_results["results"]:
|
||||
print "Tweet from @%s Date: %s" % (tweet['from_user'].encode('utf-8'),tweet['created_at'])
|
||||
print tweet['text'].encode('utf-8'),"\n"
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from twython import Twython
|
||||
|
||||
# Shortening URLs requires no authentication, huzzah
|
||||
shortURL = Twython.shortenURL("http://www.webs.com/")
|
||||
shortURL = Twython.shortenURL('http://www.webs.com/')
|
||||
|
||||
print shortURL
|
||||
5
examples/update_profile_image.py
Normal file
5
examples/update_profile_image.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from twython import Twython
|
||||
|
||||
# Requires Authentication as of Twitter API v1.1
|
||||
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
|
||||
twitter.updateProfileImage('myImage.png')
|
||||
9
examples/update_status.py
Normal file
9
examples/update_status.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from twython import Twython, TwythonError
|
||||
|
||||
# Requires Authentication as of Twitter API v1.1
|
||||
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
|
||||
|
||||
try:
|
||||
twitter.updateStatus(status='See how easy this was?')
|
||||
except TwythonError as e:
|
||||
print e
|
||||
19
setup.py
19
setup.py
|
|
@ -1,20 +1,31 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
from twython.version import __version__
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
|
||||
__version__ = '2.7.3'
|
||||
|
||||
packages = [
|
||||
'twython'
|
||||
]
|
||||
|
||||
if sys.argv[-1] == 'publish':
|
||||
os.system('python setup.py sdist upload')
|
||||
sys.exit()
|
||||
|
||||
setup(
|
||||
# Basic package information.
|
||||
name='twython',
|
||||
version=__version__,
|
||||
packages=find_packages(),
|
||||
packages=packages,
|
||||
|
||||
# Packaging options.
|
||||
include_package_data=True,
|
||||
|
||||
# Package dependencies.
|
||||
install_requires=['requests>=1.0.0, <2.0.0', 'requests_oauthlib==0.3.0'],
|
||||
install_requires=['requests==1.2.0', 'requests_oauthlib==0.3.0'],
|
||||
|
||||
# Metadata for PyPI.
|
||||
author='Ryan McGrath',
|
||||
|
|
|
|||
|
|
@ -1,2 +1,23 @@
|
|||
from twython import Twython
|
||||
# ______ __ __
|
||||
# /_ __/_ __ __ __ / /_ / /_ ____ ____
|
||||
# / / | | /| / // / / // __// __ \ / __ \ / __ \
|
||||
# / / | |/ |/ // /_/ // /_ / / / // /_/ // / / /
|
||||
# /_/ |__/|__/ \__, / \__//_/ /_/ \____//_/ /_/
|
||||
# /____/
|
||||
|
||||
"""
|
||||
Twython
|
||||
-------
|
||||
|
||||
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.
|
||||
|
||||
Questions, comments? ryan@venodesigns.net
|
||||
"""
|
||||
|
||||
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
|
||||
|
||||
from .twython import Twython
|
||||
from .exceptions import TwythonError, TwythonRateLimitError, TwythonAuthError
|
||||
|
|
|
|||
5
twython/advisory.py
Normal file
5
twython/advisory.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class TwythonDeprecationWarning(DeprecationWarning):
|
||||
"""Custom DeprecationWarning to be raised when methods/variables are being deprecated in Twython.
|
||||
Python 2.7 > ignores DeprecationWarning so we want to specifcally bubble up ONLY Twython Deprecation Warnings
|
||||
"""
|
||||
pass
|
||||
37
twython/compat.py
Normal file
37
twython/compat.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import sys
|
||||
|
||||
_ver = sys.version_info
|
||||
|
||||
#: Python 2.x?
|
||||
is_py2 = (_ver[0] == 2)
|
||||
|
||||
#: Python 3.x?
|
||||
is_py3 = (_ver[0] == 3)
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
if is_py2:
|
||||
from urllib import urlencode, quote_plus
|
||||
try:
|
||||
from urlparse import parse_qsl
|
||||
except ImportError:
|
||||
from cgi import parse_qsl
|
||||
|
||||
builtin_str = str
|
||||
bytes = str
|
||||
str = unicode
|
||||
basestring = basestring
|
||||
numeric_types = (int, long, float)
|
||||
|
||||
|
||||
elif is_py3:
|
||||
from urllib.parse import urlencode, quote_plus, parse_qsl
|
||||
|
||||
builtin_str = str
|
||||
str = str
|
||||
bytes = bytes
|
||||
basestring = (str, bytes)
|
||||
numeric_types = (int, float)
|
||||
|
|
@ -1,23 +1,20 @@
|
|||
"""
|
||||
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
|
||||
{{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.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.1 (said defaulting takes place at conversion time).
|
||||
|
||||
This map is organized the order functions are documented at:
|
||||
https://dev.twitter.com/docs/api/1.1
|
||||
This map is organized the order functions are documented at:
|
||||
https://dev.twitter.com/docs/api/1.1
|
||||
"""
|
||||
|
||||
# Base Twitter API url, no need to repeat this junk...
|
||||
base_url = 'http://api.twitter.com/{{version}}'
|
||||
|
||||
api_table = {
|
||||
# Timelines
|
||||
'getMentionsTimeline': {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from twitter_endpoints import twitter_http_status_codes
|
||||
from .endpoints import twitter_http_status_codes
|
||||
|
||||
|
||||
class TwythonError(Exception):
|
||||
|
|
|
|||
|
|
@ -1,40 +1,23 @@
|
|||
"""
|
||||
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.
|
||||
|
||||
Questions, comments? ryan@venodesigns.net
|
||||
"""
|
||||
|
||||
__author__ = "Ryan McGrath <ryan@venodesigns.net>"
|
||||
__version__ = "2.7.3"
|
||||
|
||||
import urllib
|
||||
import re
|
||||
import warnings
|
||||
|
||||
import requests
|
||||
from requests_oauthlib import OAuth1
|
||||
|
||||
try:
|
||||
from urlparse import parse_qsl
|
||||
except ImportError:
|
||||
from cgi import parse_qsl
|
||||
|
||||
# Twython maps keyword based arguments to Twitter API endpoints. The endpoints
|
||||
# table is a file with a dictionary of every API endpoint that Twython supports.
|
||||
from twitter_endpoints import base_url, api_table
|
||||
from .advisory import TwythonDeprecationWarning
|
||||
from .compat import json, urlencode, parse_qsl, quote_plus
|
||||
from .endpoints import api_table
|
||||
from .exceptions import TwythonError, TwythonAuthError, TwythonRateLimitError
|
||||
from .version import __version__
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
warnings.simplefilter('always', TwythonDeprecationWarning) # For Python 2.7 >
|
||||
|
||||
|
||||
class Twython(object):
|
||||
def __init__(self, app_key=None, app_secret=None, oauth_token=None, oauth_token_secret=None,
|
||||
headers=None, callback_url=None, twitter_token=None, twitter_secret=None, proxies=None, version='1.1'):
|
||||
def __init__(self, app_key=None, app_secret=None, oauth_token=None,
|
||||
oauth_token_secret=None, headers=None, proxies=None,
|
||||
version='1.1', callback_url=None, ssl_verify=True,
|
||||
twitter_token=None, twitter_secret=None):
|
||||
"""Instantiates an instance of Twython. Takes optional parameters for authentication and such (see below).
|
||||
|
||||
:param app_key: (optional) Your applications key
|
||||
|
|
@ -44,48 +27,59 @@ class Twython(object):
|
|||
:param headers: (optional) Custom headers to send along with the request
|
||||
:param callback_url: (optional) If set, will overwrite the callback url set in your application
|
||||
:param proxies: (optional) A dictionary of proxies, for example {"http":"proxy.example.org:8080", "https":"proxy.example.org:8081"}.
|
||||
:param ssl_verify: (optional) Turns off ssl verification when False. Useful if you have development server issues.
|
||||
"""
|
||||
|
||||
# Needed for hitting that there API.
|
||||
# API urls, OAuth urls and API version; needed for hitting that there API.
|
||||
self.api_version = version
|
||||
self.api_url = 'https://api.twitter.com/%s'
|
||||
self.request_token_url = self.api_url % 'oauth/request_token'
|
||||
self.access_token_url = self.api_url % 'oauth/access_token'
|
||||
self.authenticate_url = self.api_url % 'oauth/authenticate'
|
||||
|
||||
# Enforce unicode on keys and secrets
|
||||
self.app_key = app_key and unicode(app_key) or twitter_token and unicode(twitter_token)
|
||||
self.app_secret = app_key and unicode(app_secret) or twitter_secret and unicode(twitter_secret)
|
||||
|
||||
self.oauth_token = oauth_token and u'%s' % oauth_token
|
||||
self.oauth_token_secret = oauth_token_secret and u'%s' % oauth_token_secret
|
||||
self.app_key = app_key or twitter_token
|
||||
self.app_secret = app_secret or twitter_secret
|
||||
self.oauth_token = oauth_token
|
||||
self.oauth_token_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 or {'User-Agent': 'Twython v' + __version__}
|
||||
if twitter_token or twitter_secret:
|
||||
warnings.warn(
|
||||
'Instead of twitter_token or twitter_secret, please use app_key or app_secret (respectively).',
|
||||
TwythonDeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
# Allow for unauthenticated requests
|
||||
self.client = requests.Session()
|
||||
self.client.proxies = proxies
|
||||
if callback_url:
|
||||
warnings.warn(
|
||||
'Please pass callback_url to the get_authentication_tokens method rather than Twython.__init__',
|
||||
TwythonDeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
self.headers = {'User-Agent': 'Twython v' + __version__}
|
||||
if headers:
|
||||
self.headers.update(headers)
|
||||
|
||||
# Generate OAuth authentication object for the request
|
||||
# If no keys/tokens are passed to __init__, self.auth=None allows for
|
||||
# unauthenticated requests, although I think all v1.1 requests need auth
|
||||
self.auth = None
|
||||
if self.app_key is not None and self.app_secret is not None and \
|
||||
self.oauth_token is None and self.oauth_token_secret is None:
|
||||
self.auth = OAuth1(self.app_key, self.app_secret)
|
||||
|
||||
if self.app_key is not None and self.app_secret is not None and \
|
||||
self.oauth_token is None and self.oauth_token_secret is None:
|
||||
self.oauth_token is not None and self.oauth_token_secret is not None:
|
||||
self.auth = OAuth1(self.app_key, self.app_secret,
|
||||
signature_type='auth_header')
|
||||
self.oauth_token, self.oauth_token_secret)
|
||||
|
||||
if self.app_key is not None and self.app_secret is not None and \
|
||||
self.oauth_token is not None and self.oauth_token_secret is not None:
|
||||
self.auth = OAuth1(self.app_key, self.app_secret,
|
||||
self.oauth_token, self.oauth_token_secret,
|
||||
signature_type='auth_header')
|
||||
|
||||
if self.auth is not None:
|
||||
self.client = requests.Session()
|
||||
self.client.headers = self.headers
|
||||
self.client.auth = self.auth
|
||||
self.client.proxies = proxies
|
||||
self.client = requests.Session()
|
||||
self.client.headers = self.headers
|
||||
self.client.proxies = proxies
|
||||
self.client.auth = self.auth
|
||||
self.client.verify = ssl_verify
|
||||
|
||||
# register available funcs to allow listing name when debugging.
|
||||
def setFunc(key):
|
||||
|
|
@ -101,8 +95,8 @@ class Twython(object):
|
|||
fn = api_table[api_call]
|
||||
url = re.sub(
|
||||
'\{\{(?P<m>[a-zA-Z_]+)\}\}',
|
||||
lambda m: "%s" % kwargs.get(m.group(1), self.api_version),
|
||||
base_url + fn['url']
|
||||
lambda m: "%s" % kwargs.get(m.group(1)),
|
||||
self.api_url % self.api_version + fn['url']
|
||||
)
|
||||
|
||||
content = self._request(url, method=fn['method'], params=kwargs)
|
||||
|
|
@ -114,15 +108,7 @@ class Twython(object):
|
|||
code twice, right? ;)
|
||||
'''
|
||||
method = method.lower()
|
||||
if not method in ('get', 'post'):
|
||||
raise TwythonError('Method must be of GET or POST')
|
||||
|
||||
params = params or {}
|
||||
# requests doesn't like items that can't be converted to unicode,
|
||||
# so let's be nice and do that for the user
|
||||
for k, v in params.items():
|
||||
if isinstance(v, (int, bool)):
|
||||
params[k] = u'%s' % v
|
||||
|
||||
func = getattr(self.client, method)
|
||||
if method == 'get':
|
||||
|
|
@ -176,7 +162,7 @@ class Twython(object):
|
|||
error_code=response.status_code,
|
||||
retry_after=response.headers.get('retry-after'))
|
||||
|
||||
# if we have a json error here, then it's not an official TwitterAPI error
|
||||
# if we have a json error here, then it's not an official Twitter API error
|
||||
if json_error and not response.status_code in (200, 201, 202):
|
||||
raise TwythonError('Response was not valid JSON, unable to decode.')
|
||||
|
||||
|
|
@ -228,23 +214,23 @@ class Twython(object):
|
|||
return self._last_call['headers'][header]
|
||||
return self._last_call
|
||||
|
||||
def get_authentication_tokens(self, force_login=False, screen_name=''):
|
||||
"""Returns an authorization URL for a user to hit.
|
||||
def get_authentication_tokens(self, callback_url=None, force_login=False, screen_name=''):
|
||||
"""Returns a dict including an authorization URL (auth_url) to direct a user to
|
||||
|
||||
:param callback_url: (optional.. for now) Url the user is returned to after they authorize your app
|
||||
:param force_login: (optional) Forces the user to enter their credentials to ensure the correct users account is authorized.
|
||||
:param app_secret: (optional) If forced_login is set OR user is not currently logged in, Prefills the username input box of the OAuth login screen with the given value
|
||||
"""
|
||||
request_args = {}
|
||||
if self.callback_url:
|
||||
request_args['oauth_callback'] = self.callback_url
|
||||
|
||||
callback_url = callback_url or self.callback_url
|
||||
request_args = {'oauth_callback': callback_url}
|
||||
response = self.client.get(self.request_token_url, params=request_args)
|
||||
|
||||
if response.status_code == 401:
|
||||
raise TwythonAuthError(response.content, error_code=response.status_code)
|
||||
elif response.status_code != 200:
|
||||
raise TwythonError(response.content, error_code=response.status_code)
|
||||
|
||||
request_tokens = dict(parse_qsl(response.content))
|
||||
request_tokens = dict(parse_qsl(response.content.decode('utf-8')))
|
||||
if not request_tokens:
|
||||
raise TwythonError('Unable to decode request tokens.')
|
||||
|
||||
|
|
@ -261,18 +247,20 @@ class Twython(object):
|
|||
})
|
||||
|
||||
# Use old-style callback argument if server didn't accept new-style
|
||||
if self.callback_url and not oauth_callback_confirmed:
|
||||
if callback_url and not oauth_callback_confirmed:
|
||||
auth_url_params['oauth_callback'] = self.callback_url
|
||||
|
||||
request_tokens['auth_url'] = self.authenticate_url + '?' + urllib.urlencode(auth_url_params)
|
||||
request_tokens['auth_url'] = self.authenticate_url + '?' + urlencode(auth_url_params)
|
||||
|
||||
return request_tokens
|
||||
|
||||
def get_authorized_tokens(self, oauth_verifier):
|
||||
"""Returns authorized tokens after they go through the auth_url phase.
|
||||
|
||||
:param oauth_verifier: (required) The oauth_verifier retrieved from the callback url querystring
|
||||
"""
|
||||
response = self.client.get(self.access_token_url, params={'oauth_verifier': oauth_verifier})
|
||||
authorized_tokens = dict(parse_qsl(response.content))
|
||||
authorized_tokens = dict(parse_qsl(response.content.decode('utf-8')))
|
||||
if not authorized_tokens:
|
||||
raise TwythonError('Unable to decode authorized tokens.')
|
||||
|
||||
|
|
@ -285,7 +273,7 @@ class Twython(object):
|
|||
# ------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@staticmethod
|
||||
def shortenURL(url_to_shorten, shortener='http://is.gd/api.php'):
|
||||
def shortenURL(url_to_shorten, shortener='http://is.gd/create.php'):
|
||||
"""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. ;)
|
||||
|
|
@ -294,11 +282,18 @@ class Twython(object):
|
|||
:param shortener: (optional) In case you want to use a different
|
||||
URL shortening service
|
||||
"""
|
||||
warnings.warn(
|
||||
'With requests it\'s easy enough for a developer to implement url shortenting themselves. Please see: https://github.com/ryanmcgrath/twython/issues/184',
|
||||
TwythonDeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
if shortener == '':
|
||||
raise TwythonError('Please provide a URL shortening service.')
|
||||
|
||||
request = requests.get(shortener, params={
|
||||
'query': url_to_shorten
|
||||
'format': 'json',
|
||||
'url': url_to_shorten
|
||||
})
|
||||
|
||||
if request.status_code in [301, 201, 200]:
|
||||
|
|
@ -308,7 +303,7 @@ 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), quote_plus(Twython.unicode2utf8(value))) for (key, value) in params.iteritems()])
|
||||
|
||||
def searchGen(self, search_query, **kwargs):
|
||||
""" Returns a generator of tweets that match a specified query.
|
||||
|
|
@ -330,37 +325,19 @@ class Twython(object):
|
|||
for tweet in content['results']:
|
||||
yield tweet
|
||||
|
||||
if 'page' not in kwargs:
|
||||
kwargs['page'] = '2'
|
||||
else:
|
||||
try:
|
||||
kwargs['page'] = int(kwargs['page'])
|
||||
kwargs['page'] += 1
|
||||
kwargs['page'] = str(kwargs['page'])
|
||||
except TypeError:
|
||||
raise TwythonError("searchGen() exited because page takes type str")
|
||||
try:
|
||||
kwargs['page'] = 2 if not 'page' in kwargs else (int(kwargs['page']) + 1)
|
||||
except (TypeError, ValueError):
|
||||
raise TwythonError('Unable to generate next page of search results, `page` is not a number.')
|
||||
|
||||
for tweet in self.searchGen(search_query, **kwargs):
|
||||
yield tweet
|
||||
|
||||
def bulkUserLookup(self, **kwargs):
|
||||
"""Stub for a method that has been deprecated, kept for now to raise errors
|
||||
properly if people are relying on this (which they are...).
|
||||
"""
|
||||
warnings.warn(
|
||||
"This function has been deprecated. Please migrate to .lookupUser() - params should be the same.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
# The following methods are apart from the other Account methods,
|
||||
# because they rely on a whole multipart-data posting function set.
|
||||
|
||||
## Media Uploading functions ##############################################
|
||||
|
||||
def _media_update(self, url, file_, **params):
|
||||
return self.post(url, params=params, files=file_)
|
||||
|
||||
def updateProfileBackgroundImage(self, file_, version='1.1', **params):
|
||||
"""Updates the authenticating user's profile background image.
|
||||
|
||||
|
|
@ -372,10 +349,11 @@ class Twython(object):
|
|||
**params - You may pass items that are stated in this doc
|
||||
(https://dev.twitter.com/docs/api/1.1/post/account/update_profile_background_image)
|
||||
"""
|
||||
url = 'https://api.twitter.com/%s/account/update_profile_background_image.json' % version
|
||||
return self._media_update(url,
|
||||
{'image': (file_, open(file_, 'rb'))},
|
||||
**params)
|
||||
|
||||
return self.post('account/update_profile_background_image',
|
||||
params=params,
|
||||
files={'image': (file_, open(file_, 'rb'))},
|
||||
version=version)
|
||||
|
||||
def updateProfileImage(self, file_, version='1.1', **params):
|
||||
"""Updates the authenticating user's profile image (avatar).
|
||||
|
|
@ -387,10 +365,11 @@ class Twython(object):
|
|||
**params - You may pass items that are stated in this doc
|
||||
(https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image)
|
||||
"""
|
||||
url = 'https://api.twitter.com/%s/account/update_profile_image.json' % version
|
||||
return self._media_update(url,
|
||||
{'image': (file_, open(file_, 'rb'))},
|
||||
**params)
|
||||
|
||||
return self.post('account/update_profile_image',
|
||||
params=params,
|
||||
files={'image': (file_, open(file_, 'rb'))},
|
||||
version=version)
|
||||
|
||||
def updateStatusWithMedia(self, file_, version='1.1', **params):
|
||||
"""Updates the users status with media
|
||||
|
|
@ -403,10 +382,10 @@ class Twython(object):
|
|||
(https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media)
|
||||
"""
|
||||
|
||||
url = 'https://api.twitter.com/%s/statuses/update_with_media.json' % version
|
||||
return self._media_update(url,
|
||||
{'media': (file_, open(file_, 'rb'))},
|
||||
**params)
|
||||
return self.post('statuses/update_with_media',
|
||||
params=params,
|
||||
files={'media': (file_, open(file_, 'rb'))},
|
||||
version=version)
|
||||
|
||||
def updateProfileBannerImage(self, file_, version='1.1', **params):
|
||||
"""Updates the users profile banner
|
||||
|
|
@ -416,23 +395,16 @@ class Twython(object):
|
|||
only API version for Twitter that supports this call
|
||||
|
||||
**params - You may pass items that are taken in this doc
|
||||
(https://dev.twitter.com/docs/api/1/post/account/update_profile_banner)
|
||||
(https://dev.twitter.com/docs/api/1.1/post/account/update_profile_banner)
|
||||
"""
|
||||
url = 'https://api.twitter.com/%s/account/update_profile_banner.json' % version
|
||||
return self._media_update(url,
|
||||
{'banner': (file_, open(file_, 'rb'))},
|
||||
**params)
|
||||
|
||||
return self.post('account/update_profile_banner',
|
||||
params=params,
|
||||
files={'banner': (file_, open(file_, 'rb'))},
|
||||
version=version)
|
||||
|
||||
###########################################################################
|
||||
|
||||
def getProfileImageUrl(self, username, size='normal', version='1'):
|
||||
warnings.warn(
|
||||
"This function has been deprecated. Twitter API v1.1 will not have a dedicated endpoint \
|
||||
for this functionality.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def stream(data, callback):
|
||||
"""A Streaming API endpoint, because requests (by Kenneth Reitz)
|
||||
|
|
|
|||
1
twython/version.py
Normal file
1
twython/version.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
__version__ = '2.8.0'
|
||||
|
|
@ -1 +0,0 @@
|
|||
from .twython import Twython
|
||||
|
|
@ -1,334 +0,0 @@
|
|||
"""
|
||||
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:
|
||||
|
||||
{{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.
|
||||
|
||||
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...
|
||||
base_url = 'http://api.twitter.com/{{version}}'
|
||||
|
||||
api_table = {
|
||||
'getRateLimitStatus': {
|
||||
'url': '/account/rate_limit_status.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
'verifyCredentials': {
|
||||
'url': '/account/verify_credentials.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
'endSession' : {
|
||||
'url': '/account/end_session.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
|
||||
# Timeline methods
|
||||
'getPublicTimeline': {
|
||||
'url': '/statuses/public_timeline.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getHomeTimeline': {
|
||||
'url': '/statuses/home_timeline.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getUserTimeline': {
|
||||
'url': '/statuses/user_timeline.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getFriendsTimeline': {
|
||||
'url': '/statuses/friends_timeline.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Interfacing with friends/followers
|
||||
'getUserMentions': {
|
||||
'url': '/statuses/mentions.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getFriendsStatus': {
|
||||
'url': '/statuses/friends.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getFollowersStatus': {
|
||||
'url': '/statuses/followers.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'createFriendship': {
|
||||
'url': '/friendships/create.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'destroyFriendship': {
|
||||
'url': '/friendships/destroy.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'getFriendsIDs': {
|
||||
'url': '/friends/ids.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getFollowersIDs': {
|
||||
'url': '/followers/ids.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getIncomingFriendshipIDs': {
|
||||
'url': '/friendships/incoming.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getOutgoingFriendshipIDs': {
|
||||
'url': '/friendships/outgoing.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Retweets
|
||||
'reTweet': {
|
||||
'url': '/statuses/retweet/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'getRetweets': {
|
||||
'url': '/statuses/retweets/{{id}}.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'retweetedOfMe': {
|
||||
'url': '/statuses/retweets_of_me.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'retweetedByMe': {
|
||||
'url': '/statuses/retweeted_by_me.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'retweetedToMe': {
|
||||
'url': '/statuses/retweeted_to_me.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# User methods
|
||||
'showUser': {
|
||||
'url': '/users/show.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'searchUsers': {
|
||||
'url': '/users/search.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
'lookupUser': {
|
||||
'url': '/users/lookup.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Status methods - showing, updating, destroying, etc.
|
||||
'showStatus': {
|
||||
'url': '/statuses/show/{{id}}.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'updateStatus': {
|
||||
'url': '/statuses/update.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'destroyStatus': {
|
||||
'url': '/statuses/destroy/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
|
||||
# Direct Messages - getting, sending, effing, etc.
|
||||
'getDirectMessages': {
|
||||
'url': '/direct_messages.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getSentMessages': {
|
||||
'url': '/direct_messages/sent.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'sendDirectMessage': {
|
||||
'url': '/direct_messages/new.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'destroyDirectMessage': {
|
||||
'url': '/direct_messages/destroy/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
|
||||
# Friendship methods
|
||||
'checkIfFriendshipExists': {
|
||||
'url': '/friendships/exists.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'showFriendship': {
|
||||
'url': '/friendships/show.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Profile methods
|
||||
'updateProfile': {
|
||||
'url': '/account/update_profile.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'updateProfileColors': {
|
||||
'url': '/account/update_profile_colors.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'myTotals': {
|
||||
'url' : '/account/totals.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Favorites methods
|
||||
'getFavorites': {
|
||||
'url': '/favorites.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'createFavorite': {
|
||||
'url': '/favorites/create/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'destroyFavorite': {
|
||||
'url': '/favorites/destroy/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
|
||||
# Blocking methods
|
||||
'createBlock': {
|
||||
'url': '/blocks/create/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'destroyBlock': {
|
||||
'url': '/blocks/destroy/{{id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'getBlocking': {
|
||||
'url': '/blocks/blocking.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getBlockedIDs': {
|
||||
'url': '/blocks/blocking/ids.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'checkIfBlockExists': {
|
||||
'url': '/blocks/exists.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Trending methods
|
||||
'getCurrentTrends': {
|
||||
'url': '/trends/current.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getDailyTrends': {
|
||||
'url': '/trends/daily.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getWeeklyTrends': {
|
||||
'url': '/trends/weekly.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'availableTrends': {
|
||||
'url': '/trends/available.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'trendsByLocation': {
|
||||
'url': '/trends/{{woeid}}.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# Saved Searches
|
||||
'getSavedSearches': {
|
||||
'url': '/saved_searches.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'showSavedSearch': {
|
||||
'url': '/saved_searches/show/{{id}}.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'createSavedSearch': {
|
||||
'url': '/saved_searches/create.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'destroySavedSearch': {
|
||||
'url': '/saved_searches/destroy/{{id}}.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
|
||||
# List API methods/endpoints. Fairly exhaustive and annoying in general. ;P
|
||||
'createList': {
|
||||
'url': '/{{username}}/lists.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'updateList': {
|
||||
'url': '/{{username}}/lists/{{list_id}}.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'showLists': {
|
||||
'url': '/{{username}}/lists.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getListMemberships': {
|
||||
'url': '/{{username}}/lists/memberships.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getListSubscriptions': {
|
||||
'url': '/{{username}}/lists/subscriptions.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'deleteList': {
|
||||
'url': '/{{username}}/lists/{{list_id}}.json',
|
||||
'method': 'DELETE',
|
||||
},
|
||||
'getListTimeline': {
|
||||
'url': '/{{username}}/lists/{{list_id}}/statuses.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'getSpecificList': {
|
||||
'url': '/{{username}}/lists/{{list_id}}/statuses.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'addListMember': {
|
||||
'url': '/{{username}}/{{list_id}}/members.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'getListMembers': {
|
||||
'url': '/{{username}}/{{list_id}}/members.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'deleteListMember': {
|
||||
'url': '/{{username}}/{{list_id}}/members.json',
|
||||
'method': 'DELETE',
|
||||
},
|
||||
'getListSubscribers': {
|
||||
'url': '/{{username}}/{{list_id}}/subscribers.json',
|
||||
'method': 'GET',
|
||||
},
|
||||
'subscribeToList': {
|
||||
'url': '/{{username}}/{{list_id}}/subscribers.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'unsubscribeFromList': {
|
||||
'url': '/{{username}}/{{list_id}}/subscribers.json',
|
||||
'method': 'DELETE',
|
||||
},
|
||||
|
||||
# The one-offs
|
||||
'notificationFollow': {
|
||||
'url': '/notifications/follow/follow.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'notificationLeave': {
|
||||
'url': '/notifications/leave/leave.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'updateDeliveryService': {
|
||||
'url': '/account/update_delivery_device.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
'reportSpam': {
|
||||
'url': '/report_spam.json',
|
||||
'method': 'POST',
|
||||
},
|
||||
}
|
||||
|
|
@ -1,513 +0,0 @@
|
|||
#!/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.
|
||||
|
||||
Questions, comments? ryan@venodesigns.net
|
||||
"""
|
||||
|
||||
__author__ = "Ryan McGrath <ryan@venodesigns.net>"
|
||||
__version__ = "1.4.7"
|
||||
|
||||
import cgi
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import urllib.parse
|
||||
import http.client
|
||||
import httplib2
|
||||
import mimetypes
|
||||
import re
|
||||
import inspect
|
||||
import email.generator
|
||||
|
||||
import oauth2 as oauth
|
||||
|
||||
# Twython maps keyword based arguments to Twitter API endpoints. The endpoints
|
||||
# table is a file with a dictionary of every API endpoint that Twython supports.
|
||||
from twitter_endpoints import base_url, api_table
|
||||
|
||||
from urllib.error 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:
|
||||
# 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
|
||||
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):
|
||||
"""
|
||||
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:
|
||||
|
||||
from twython import TwythonError, APILimit, AuthError
|
||||
"""
|
||||
def __init__(self, msg, error_code=None):
|
||||
self.msg = msg
|
||||
if error_code == 400:
|
||||
raise APILimit(msg)
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.msg)
|
||||
|
||||
|
||||
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
|
||||
this matter beyond telling you that you've done goofed.
|
||||
"""
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
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, 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.
|
||||
"""
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
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={}):
|
||||
"""setup(self, oauth_token = None, headers = None)
|
||||
|
||||
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}
|
||||
|
||||
** 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 = 'https://twitter.com/oauth/request_token'
|
||||
self.access_token_url = 'https://twitter.com/oauth/access_token'
|
||||
self.authorize_url = 'https://twitter.com/oauth/authorize'
|
||||
self.authenticate_url = 'https://twitter.com/oauth/authenticate'
|
||||
self.twitter_token = twitter_token
|
||||
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
|
||||
if self.headers is None:
|
||||
self.headers = {'User-agent': 'Twython Python Twitter Library v1.3'}
|
||||
|
||||
consumer = None
|
||||
token = None
|
||||
|
||||
if self.twitter_token is not None and self.twitter_secret is not None:
|
||||
consumer = oauth.Consumer(self.twitter_token, self.twitter_secret)
|
||||
|
||||
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.
|
||||
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.
|
||||
self.client = httplib2.Http(**client_args)
|
||||
def setFunc(key):
|
||||
return lambda **kwargs: self._constructFunc(key, **kwargs)
|
||||
# register available funcs to allow listing name when debugging.
|
||||
for key in api_table.keys():
|
||||
self.__dict__[key] = setFunc(key)
|
||||
|
||||
def _constructFunc(self, api_call, **kwargs):
|
||||
# Go through and replace any mustaches that are in our API url.
|
||||
fn = api_table[api_call]
|
||||
base = re.sub(
|
||||
'\{\{(?P<m>[a-zA-Z_]+)\}\}',
|
||||
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
|
||||
if fn['method'] == 'POST':
|
||||
resp, content = self.client.request(base, fn['method'], urllib.parse.urlencode(dict([k, Twython.encode(v)] for k, v in list(kwargs.items()))), headers = self.headers)
|
||||
else:
|
||||
url = base + "?" + "&".join(["%s=%s" %(key, value) for (key, value) in list(kwargs.items())])
|
||||
resp, content = self.client.request(url, fn['method'], headers = self.headers)
|
||||
|
||||
return simplejson.loads(content.decode('utf-8'))
|
||||
|
||||
def get_authentication_tokens(self):
|
||||
"""
|
||||
get_auth_url(self)
|
||||
|
||||
Returns an authorization URL for a user to hit.
|
||||
"""
|
||||
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, 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))
|
||||
|
||||
try:
|
||||
request_tokens = dict(urllib.parse.parse_qsl(content))
|
||||
except:
|
||||
request_tokens = dict(cgi.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'],
|
||||
}
|
||||
|
||||
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.parse.urlencode(auth_url_params)
|
||||
|
||||
return request_tokens
|
||||
|
||||
def get_authorized_tokens(self):
|
||||
"""
|
||||
get_authorized_tokens
|
||||
|
||||
Returns authorized tokens after they go through the auth_url phase.
|
||||
"""
|
||||
resp, content = self.client.request(self.access_token_url, "GET")
|
||||
try:
|
||||
return dict(urllib.parse.parse_qsl(content))
|
||||
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.
|
||||
# ------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@staticmethod
|
||||
def constructApiURL(base_url, params):
|
||||
return base_url + "?" + "&".join(["%s=%s" % (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")
|
||||
|
||||
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.
|
||||
"""
|
||||
try:
|
||||
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))
|
||||
|
||||
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.
|
||||
|
||||
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("https://api.twitter.com/%d/users/lookup.json" % version, kwargs)
|
||||
try:
|
||||
resp, content = self.client.request(lookupURL, "POST", headers = self.headers)
|
||||
return simplejson.loads(content.decode('utf-8'))
|
||||
except HTTPError as e:
|
||||
raise TwythonError("bulkUserLookup() failed with a %s error code." % repr(e.code), e.code)
|
||||
|
||||
def search(self, **kwargs):
|
||||
"""search(search_query, **kwargs)
|
||||
|
||||
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.
|
||||
|
||||
e.g x.search(q = "jjndf", page = '2')
|
||||
"""
|
||||
searchURL = Twython.constructApiURL("https://search.twitter.com/search.json", kwargs)
|
||||
try:
|
||||
resp, content = self.client.request(searchURL, "GET", headers = self.headers)
|
||||
return simplejson.loads(content.decode('utf-8'))
|
||||
except HTTPError as e:
|
||||
raise TwythonError("getSearchTimeline() failed with a %s error code." % repr(e.code), e.code)
|
||||
|
||||
def searchTwitter(self, **kwargs):
|
||||
"""use search() ,this is a fall back method to support searchTwitter()
|
||||
"""
|
||||
return self.search(**kwargs)
|
||||
|
||||
def searchGen(self, search_query, **kwargs):
|
||||
"""searchGen(search_query, **kwargs)
|
||||
|
||||
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.
|
||||
|
||||
e.g x.searchGen("python", page="2") or
|
||||
x.searchGen(search_query = "python", page = "2")
|
||||
"""
|
||||
searchURL = Twython.constructApiURL("https://search.twitter.com/search.json?q=%s" % Twython.unicode2utf8(search_query), kwargs)
|
||||
try:
|
||||
resp, content = self.client.request(searchURL, "GET", headers = self.headers)
|
||||
data = simplejson.loads(content.decode('utf-8'))
|
||||
except HTTPError as e:
|
||||
raise TwythonError("searchGen() failed with a %s error code." % repr(e.code), e.code)
|
||||
|
||||
if not data['results']:
|
||||
raise StopIteration
|
||||
|
||||
for tweet in data['results']:
|
||||
yield tweet
|
||||
|
||||
if 'page' not in kwargs:
|
||||
kwargs['page'] = '2'
|
||||
else:
|
||||
try:
|
||||
kwargs['page'] = int(kwargs['page'])
|
||||
kwargs['page'] += 1
|
||||
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" %\
|
||||
repr(e.code), e.code)
|
||||
|
||||
for tweet in self.searchGen(search_query, **kwargs):
|
||||
yield tweet
|
||||
|
||||
def searchTwitterGen(self, search_query, **kwargs):
|
||||
"""use searchGen(), this is a fallback method to support
|
||||
searchTwitterGen()"""
|
||||
return self.searchGen(search_query, **kwargs)
|
||||
|
||||
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).
|
||||
|
||||
**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.
|
||||
"""
|
||||
try:
|
||||
resp, content = self.client.request("https://api.twitter.com/%d/%s/%s/members/%s.json" % (version, username, list_id, repr(id)), headers = self.headers)
|
||||
return simplejson.loads(content.decode('utf-8'))
|
||||
except HTTPError as e:
|
||||
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).
|
||||
|
||||
**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.
|
||||
"""
|
||||
try:
|
||||
resp, content = self.client.request("https://api.twitter.com/%d/%s/%s/following/%s.json" % (version, username, list_id, repr(id)), headers = self.headers)
|
||||
return simplejson.loads(content.decode('utf-8'))
|
||||
except HTTPError as e:
|
||||
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.
|
||||
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.
|
||||
"""
|
||||
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("https://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)
|
||||
|
||||
def updateProfileImage(self, filename, version = 1):
|
||||
""" updateProfileImage(filename)
|
||||
|
||||
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.
|
||||
"""
|
||||
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("https://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)
|
||||
|
||||
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.
|
||||
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)
|
||||
if 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):
|
||||
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 encode_multipart_formdata(fields, files):
|
||||
BOUNDARY = email.generator._make_boundary()
|
||||
CRLF = '\r\n'
|
||||
L = []
|
||||
for (key, value) in fields:
|
||||
L.append('--' + BOUNDARY)
|
||||
L.append('Content-Disposition: form-data; name="%s"' % key)
|
||||
L.append('')
|
||||
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('')
|
||||
L.append(value)
|
||||
L.append('--' + BOUNDARY + '--')
|
||||
L.append('')
|
||||
body = CRLF.join(L)
|
||||
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
|
||||
return content_type, body
|
||||
|
||||
@staticmethod
|
||||
def unicode2utf8(text):
|
||||
try:
|
||||
if isinstance(text, str):
|
||||
text = text.encode('utf-8')
|
||||
except:
|
||||
pass
|
||||
return text
|
||||
|
||||
@staticmethod
|
||||
def encode(text):
|
||||
if isinstance(text, str):
|
||||
return Twython.unicode2utf8(text)
|
||||
return str(text)
|
||||
Loading…
Add table
Add a link
Reference in a new issue