pax_global_header00006660000000000000000000000064121574122650014517gustar00rootroot0000000000000052 comment=d1f2dc240d612a37d926f823b5ae302704b22045 tweepy-2.1/000077500000000000000000000000001215741226500126765ustar00rootroot00000000000000tweepy-2.1/.gitignore000066400000000000000000000000521215741226500146630ustar00rootroot00000000000000*.pyc *.swp build/ dist/ tweepy.egg-info/ tweepy-2.1/.travis.yml000066400000000000000000000020451215741226500150100ustar00rootroot00000000000000--- script: nosetests -v tests.test_api tests.test_streaming tests.test_cursors language: python env: global: - TWITTER_USERNAME="tweepytest" - secure: |- MZv5O5E5E1074sT14wnRThOeFoDTZy+AdIUy+S6XqY/DMVWF2utSx09GLbvM EM+cnKavRLHTbKpoHPIzzhKx9DQLrxtYy+s3Rw9AeGo8K3LA7mcn4T4eCH7s RGaEkEqH4wTbe01zsmBJ9n9pALmtFgDk9WeDlpAY9wc5cAPgrZ4= - secure: |- f4nCh413cDJF3llGsUdaSz8Rw8WaEDzsSAqa7rK8IcozvS6+S82nm8A/xKYg i6A6uC081XWa6JqQ9hyTxHam3OWGAYrytELrEUfkX4ZYMM8lmQYCmnhrN9r/ FaX1haT4lRfm9r0zAYZileLVmcTg8LSZzpRowA/DH9ZO8QD76b4= - secure: |- YxZerqEAifcNS3qTbWyr2MAxDl1hMGv2p87nmw+AHP62LoTGEhHtulQdadjG CNdhLzyIY+GgRotWVu39OfVPRKyfMBFrpPdUY7tKSZ/O8Ct9792mrSvCOpOV Li/TYytFtrNQiB7yjcoaub1RabffBcLbu5YzbWN4gPrukQV03Jc= - secure: |- nKkytraqLGUm33K1GpwkjOyxACDHYw4GMvOGyDwVTX7VNwqxbkUojB7qXYoQ JjlEyFWS487IFteR87U9pt18qongJJIphaBdT9/lDVLsMWZ0Jh5ZLQfX+2jS aF2UwsrYkzBUMrqMqYCc2+X6CuswLEZTVXDAlNh+emvhxZ5faMI= python: - "2.7" branches: only: - master tweepy-2.1/ASCII_LOGO.txt000066400000000000000000000004071215741226500151100ustar00rootroot00000000000000 ______ /_ __/_ __ ___ ___ ____ __ __ / / | | /| / // _ \ / _ \ / __ \ / / / / / / | |/ |/ // __// __// /_/ // /_/ / /_/ |__/|__/ \___/ \___// .___/ \__, / /_/ /____/ tweepy-2.1/CHANGELOG.md000066400000000000000000000025301215741226500145070ustar00rootroot00000000000000Version 2.1 ----------- - Added get_oembed(). - friends() and followers() are back and updated to v1.1. - Fixed report_spam() endpoint. - Added "languages" parameter to streaming filter() method. - Added "timeout" support for API object. Ex: API(timeout=1000). - Python 2.5 no longer supported. - Added compression support. Ex: API(compression=True). - Added on_connect() callback to StreamListener. - Switched API search() to v1.1 endpoint. Some breaking changes. - Drop "page" based cursors and use "ID" based ones instead. - [Compare 2.0...master](https://github.com/tweepy/tweepy/compare/2.0...master) Version 2.0 ----------- _Dedicated in memory of Aaron Swartz_ - Twitter API 1.1 support. - Basic Authentication deprecated. - friends_timeline() removed. - mentions() removed and replaced by mentions_timeline(). - retweeted_by_user() removed. - retweeted_by_me() removed. - retweeted_to_me() removed. - retweeted_by_user() removed. - friends() removed. - followers() removed. - enable_notifications() removed. - disable_notifications() removed. - exists_block() removed. - lists() removed and replaced by lists_all(). - is_list_member() removed. - show_list_member added. - is_subscribed_list() removed. - show_list_subscriber() added. - trends_location() removed. - nearby_places() removed. tweepy-2.1/CONTRIBUTORS000066400000000000000000000010071215741226500145540ustar00rootroot00000000000000Thank you to all who have contributed to this project! If you contributed and not listed below please let me know. Aaron Swartz Adam Miskiewicz AlanBell Arthur Debert Bas Westerbaan Chris Kelly Clay McClure Ferenc Szalai Gergely Imreh Guan Yang Ivo Wetzel James Rowe Jenny Loomis Johannes Faigle Kumar Appaiah Michael (Doc) Norton Pascal Jürgens Robin Houston Sam Kaufman Thomas Bohmbach, Jr Wayne Moore Will McCutchen gilles Can Duruk Jan Schaumann (@jschauma) Stuart Powers Jeff Hull (@jsh2134) Mike (mikeandmore) tweepy-2.1/INSTALL000066400000000000000000000012351215741226500137300ustar00rootroot00000000000000easy_install ------------ easy_install tweepy setuptools - from Git repository -------------------------------- > git clone git://github.com/tweepy/tweepy.git > cd tweepy > python setup.py install (run as admin/root) setuptools - from source archive -------------------------------- Download source archive here: http://pypi.python.org/packages/source/t/tweepy/tweepy-1.2.tar.gz Extract archive > cd tweepy-1.2 > python setup.py install (run as admin/root) bundle with your application ---------------------------- You may also download a source archive or checkout via Git. Then just include the tweepy package folder inside your application's source folder. tweepy-2.1/LICENSE000066400000000000000000000020651215741226500137060ustar00rootroot00000000000000MIT License Copyright (c) 2009-2010 Joshua Roesslein Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tweepy-2.1/README.md000066400000000000000000000016051215741226500141570ustar00rootroot00000000000000Tweepy [![Build Status](https://travis-ci.org/tweepy/tweepy.png)](https://travis-ci.org/tweepy/tweepy) ====== *Twitter for Python!* Installation ------------ The easiest way to install the latest version is by using pip/easy_install to pull it from PyPI: pip install tweepy You may also use Git to clone the repository from Github and install it manually: git clone https://github.com/tweepy/tweepy.git python setup.py install **Note** only Python 2.6 and 2.7 is supported at the moment. The Python 3 family is not yet supported. Documentation ------------- - [Website (Work in-progress)](http://tweepy.github.com/) - [Twitter Developers](http://dev.twitter.com/) - [Python Package Documentation](http://packages.python.org/tweepy/html/index.html) Community --------- - [Google Group/Mailing list](http://groups.google.com/group/tweepy) - IRC Chat (Freenode.net #tweepy) tweepy-2.1/docs/000077500000000000000000000000001215741226500136265ustar00rootroot00000000000000tweepy-2.1/docs/.gitignore000066400000000000000000000000101215741226500156050ustar00rootroot00000000000000_build/ tweepy-2.1/docs/Makefile000066400000000000000000000107561215741226500152770ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Tweepy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Tweepy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Tweepy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Tweepy" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." tweepy-2.1/docs/_static/000077500000000000000000000000001215741226500152545ustar00rootroot00000000000000tweepy-2.1/docs/_static/.keep000066400000000000000000000000001215741226500161670ustar00rootroot00000000000000tweepy-2.1/docs/conf.py000066400000000000000000000156071215741226500151360ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Tweepy documentation build configuration file, created by # sphinx-quickstart on Fri Apr 15 18:57:17 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Tweepy' copyright = u'2011, Joshua Roesslein' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. sys.path.insert(0, '..') from tweepy import __version__ version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Tweepydoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Tweepy.tex', u'Tweepy Documentation', u'Joshua Roesslein', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'tweepy', u'Tweepy Documentation', [u'Joshua Roesslein'], 1) ] tweepy-2.1/docs/index.rst000066400000000000000000000004561215741226500154740ustar00rootroot00000000000000.. Tweepy documentation master file, created by sphinx-quickstart on Fri Apr 15 18:57:17 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Tweepy Documentation ==================== .. toctree:: :maxdepth: 2 install tweepy-2.1/docs/install.rst000066400000000000000000000002741215741226500160310ustar00rootroot00000000000000Installation ============ Install from PyPI:: easy_install tweepy Install from source:: git clone git://github.com/tweepy/tweepy.git cd tweepy python setup.py install tweepy-2.1/docs/make.bat000066400000000000000000000106371215741226500152420ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Tweepy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Tweepy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end tweepy-2.1/examples/000077500000000000000000000000001215741226500145145ustar00rootroot00000000000000tweepy-2.1/examples/basic_auth.py000066400000000000000000000017061215741226500171740ustar00rootroot00000000000000import tweepy # === Basic Authentication === # # *Note: Basic Authentication is deprecated and no longer supported on Twitter. # It is still provided for use in services like Status.net which still suppports it.* # # This mode of authentication requires the user provide their username and plain text password. # These credentials will then be provided for each request to the API for authentication. # You would normally fetch this in your application # by asking the user or loading from some dark place. username = "" password = "" # Create an authentication handler passing it the username and password. # We will use this object later on when creating our API object. auth = tweepy.auth.BasicAuthHandler(username, password) # Create the API object providing it the authentication handler to use. # Each request will then be authenticated using this handler. api = tweepy.API(auth) api.update_status('Updating using basic authentication via Tweepy!') tweepy-2.1/examples/oauth.py000066400000000000000000000017721215741226500162150ustar00rootroot00000000000000import tweepy # == OAuth Authentication == # # This mode of authentication is the new preferred way # of authenticating with Twitter. # The consumer keys can be found on your application's Details # page located at https://dev.twitter.com/apps (under "OAuth settings") consumer_key="" consumer_secret="" # The access tokens can be found on your applications's Details # page located at https://dev.twitter.com/apps (located # under "Your access token") access_token="" access_token_secret="" auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) # If the authentication was successful, you should # see the name of the account print out print api.me().name # If the application settings are set for "Read and Write" then # this line should tweet out the message to your account's # timeline. The "Read and Write" setting is on https://dev.twitter.com/apps api.update_status('Updating using OAuth authentication via Tweepy!') tweepy-2.1/examples/streaming.py000066400000000000000000000017161215741226500170640ustar00rootroot00000000000000from tweepy.streaming import StreamListener from tweepy import OAuthHandler from tweepy import Stream # Go to http://dev.twitter.com and create an app. # The consumer key and secret will be generated for you after consumer_key="" consumer_secret="" # After the step above, you will be redirected to your app's page. # Create an access token under the the "Your access token" section access_token="" access_token_secret="" class StdOutListener(StreamListener): """ A listener handles tweets are the received from the stream. This is a basic listener that just prints received tweets to stdout. """ def on_data(self, data): print data return True def on_error(self, status): print status if __name__ == '__main__': l = StdOutListener() auth = OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) stream = Stream(auth, l) stream.filter(track=['basketball']) tweepy-2.1/setup.py000066400000000000000000000007211215741226500144100ustar00rootroot00000000000000#!/usr/bin/env python #from distutils.core import setup from setuptools import setup, find_packages from tweepy import __version__ setup(name="tweepy", version=__version__, description="Twitter library for python", license="MIT", author="Joshua Roesslein", author_email="tweepy@googlegroups.com", url="http://github.com/tweepy/tweepy", packages = find_packages(), keywords= "twitter library", zip_safe = True) tweepy-2.1/tests/000077500000000000000000000000001215741226500140405ustar00rootroot00000000000000tweepy-2.1/tests/__init__.py000066400000000000000000000000011215741226500161400ustar00rootroot00000000000000 tweepy-2.1/tests/config.py000066400000000000000000000007441215741226500156640ustar00rootroot00000000000000import os from tweepy.auth import OAuthHandler username = os.environ.get('TWITTER_USERNAME', '') oauth_consumer_key = os.environ.get('CONSUMER_KEY', '') oauth_consumer_secret = os.environ.get('CONSUMER_SECRET', '') oauth_token = os.environ.get('ACCESS_KEY', '') oauth_token_secret = os.environ.get('ACCESS_SECRET', '') def create_auth(): auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret) auth.set_access_token(oauth_token, oauth_token_secret) return auth tweepy-2.1/tests/mock.py000066400000000000000000000003121215741226500153370ustar00rootroot00000000000000import random import string def mock_tweet(): """Generate some random tweet text.""" count = random.randint(70, 140) return ''.join([random.choice(string.letters) for i in xrange(count)]) tweepy-2.1/tests/test_api.py000066400000000000000000000267311215741226500162330ustar00rootroot00000000000000import unittest import random from time import sleep import os from nose import SkipTest from tweepy import (API, OAuthHandler, Friendship, Cursor, MemoryCache, FileCache) from config import * test_tweet_id = '266367358078169089' """Unit tests""" class TweepyErrorTests(unittest.TestCase): def testpickle(self): """Verify exceptions can be pickled and unpickled.""" import pickle from tweepy.error import TweepError e = TweepError('no reason', {'status': 200}) e2 = pickle.loads(pickle.dumps(e)) self.assertEqual(e.reason, e2.reason) self.assertEqual(e.response, e2.response) class TweepyAPITests(unittest.TestCase): def setUp(self): auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret) auth.set_access_token(oauth_token, oauth_token_secret) self.api = API(auth) self.api.retry_count = 2 self.api.retry_delay = 5 # TODO: Actually have some sort of better assertion def testgetoembed(self): data = self.api.get_oembed(test_tweet_id) self.assertEqual(data['author_name'], "Twitter") def testhometimeline(self): self.api.home_timeline() def testusertimeline(self): self.api.user_timeline() self.api.user_timeline('twitter') def testmentionstimeline(self): self.api.mentions_timeline() def testretweetsofme(self): self.api.retweets_of_me() def testretweet(self): # TODO(josh): Need a way to get random tweets to retweet. raise SkipTest() def testretweets(self): self.api.retweets(test_tweet_id) def testgetstatus(self): self.api.get_status(id=test_tweet_id) def testupdateanddestroystatus(self): # test update text = 'testing %i' % random.randint(0, 1000) update = self.api.update_status(status=text) self.assertEqual(update.text, text) # test destroy deleted = self.api.destroy_status(id=update.id) self.assertEqual(deleted.id, update.id) def testgetuser(self): u = self.api.get_user('twitter') self.assertEqual(u.screen_name, 'twitter') u = self.api.get_user(783214) self.assertEqual(u.screen_name, 'twitter') def testsearchusers(self): self.api.search_users('twitter') def testsuggestedcategories(self): self.api.suggested_categories() def testsuggestedusers(self): categories = self.api.suggested_categories() if len(categories) != 0: self.api.suggested_users(categories[0].slug) def testsuggesteduserstweets(self): categories = self.api.suggested_categories() if len(categories) != 0: self.api.suggested_users_tweets(categories[0].slug) def testme(self): me = self.api.me() self.assertEqual(me.screen_name, username) def testdirectmessages(self): self.api.direct_messages() def testsentdirectmessages(self): self.api.sent_direct_messages() def testsendanddestroydirectmessage(self): # send sent_dm = self.api.send_direct_message(username, text='test message') self.assertEqual(sent_dm.text, 'test message') self.assertEqual(sent_dm.sender.screen_name, username) self.assertEqual(sent_dm.recipient.screen_name, username) # destroy destroyed_dm = self.api.destroy_direct_message(sent_dm.id) self.assertEqual(destroyed_dm.text, sent_dm.text) self.assertEqual(destroyed_dm.id, sent_dm.id) self.assertEqual(destroyed_dm.sender.screen_name, username) self.assertEqual(destroyed_dm.recipient.screen_name, username) def testcreatedestroyfriendship(self): enemy = self.api.destroy_friendship('twitter') self.assertEqual(enemy.screen_name, 'twitter') # Wait 5 seconds to allow Twitter time # to process the friendship destroy request. sleep(5) friend = self.api.create_friendship('twitter') self.assertEqual(friend.screen_name, 'twitter') def testshowfriendship(self): source, target = self.api.show_friendship(target_screen_name='twtiter') self.assert_(isinstance(source, Friendship)) self.assert_(isinstance(target, Friendship)) def testfriendsids(self): self.api.friends_ids(username) def testfollowersids(self): self.api.followers_ids(username) def testfriends(self): self.api.friends(username) def testfollowers(self): self.api.followers(username) def testverifycredentials(self): self.assertNotEqual(self.api.verify_credentials(), False) # make sure that `me.status.entities` is not an empty dict me = self.api.verify_credentials(include_entities=True) self.assertTrue(me.status.entities) # `status` shouldn't be included me = self.api.verify_credentials(skip_status=True) self.assertFalse(hasattr(me, 'status')) def testratelimitstatus(self): self.api.rate_limit_status() """ TODO(josh): Remove once this deprecated API is gone. def testsetdeliverydevice(self): self.api.set_delivery_device('im') self.api.set_delivery_device('none') """ def testupdateprofilecolors(self): original = self.api.me() updated = self.api.update_profile_colors('000', '000', '000', '000', '000') # restore colors self.api.update_profile_colors( original.profile_background_color, original.profile_text_color, original.profile_link_color, original.profile_sidebar_fill_color, original.profile_sidebar_border_color ) self.assertEqual(updated.profile_background_color, '000000') self.assertEqual(updated.profile_text_color, '000000') self.assertEqual(updated.profile_link_color, '000000') self.assertEqual(updated.profile_sidebar_fill_color, '000000') self.assertEqual(updated.profile_sidebar_border_color, '000000') """ def testupateprofileimage(self): self.api.update_profile_image('examples/profile.png') def testupdateprofilebg(self): self.api.update_profile_background_image('examples/bg.png') """ def testupdateprofile(self): original = self.api.me() profile = { 'name': 'Tweepy test 123', 'location': 'pytopia', 'description': 'just testing things out' } updated = self.api.update_profile(**profile) self.api.update_profile( name = original.name, url = original.url, location = original.location, description = original.description ) for k,v in profile.items(): if k == 'email': continue self.assertEqual(getattr(updated, k), v) def testfavorites(self): self.api.favorites() def testcreatedestroyfavorite(self): self.api.create_favorite(4901062372) self.api.destroy_favorite(4901062372) def testcreatedestroyblock(self): self.api.create_block('twitter') self.api.destroy_block('twitter') self.api.create_friendship('twitter') # restore def testblocks(self): self.api.blocks() def testblocksids(self): self.api.blocks_ids() def testcreateupdatedestroylist(self): params = { 'owner_screen_name': username, 'slug': 'tweeps' } l = self.api.create_list(name=params['slug'], **params) l = self.api.update_list(list_id=l.id, description='updated!') self.assertEqual(l.description, 'updated!') self.api.destroy_list(list_id=l.id) def testlistsall(self): self.api.lists_all() def testlistsmemberships(self): self.api.lists_memberships() def testlistssubscriptions(self): self.api.lists_subscriptions() def testlisttimeline(self): self.api.list_timeline('applepie', 'stars') def testgetlist(self): self.api.get_list(owner_screen_name='applepie', slug='stars') def testaddremovelistmember(self): params = { 'slug': 'test', 'owner_screen_name': username, 'screen_name': 'twitter' } def assert_list(l): self.assertEqual(l.name, params['slug']) assert_list(self.api.add_list_member(**params)) assert_list(self.api.remove_list_member(**params)) def testlistmembers(self): self.api.list_members('applepie', 'stars') def testshowlistmember(self): self.assertTrue(self.api.show_list_member(owner_screen_name='applepie', slug='stars', screen_name='NathanFillion')) def testsubscribeunsubscribelist(self): params = { 'owner_screen_name': 'applepie', 'slug': 'stars' } self.api.subscribe_list(**params) self.api.unsubscribe_list(**params) def testlistsubscribers(self): self.api.list_subscribers('applepie', 'stars') def testshowlistsubscriber(self): self.assertTrue(self.api.show_list_subscriber('twitter', 'team', username)) def testsavedsearches(self): s = self.api.create_saved_search('test') self.api.saved_searches() self.assertEqual(self.api.get_saved_search(s.id).query, 'test') self.api.destroy_saved_search(s.id) def testsearch(self): self.api.search('tweepy') def testgeoapis(self): def place_name_in_list(place_name, place_list): """Return True if a given place_name is in place_list.""" return any([x.full_name.lower() == place_name.lower() for x in place_list]) twitter_hq = self.api.geo_similar_places(lat=37, long= -122, name='Twitter HQ') # Assumes that twitter_hq is first Place returned... self.assertEqual(twitter_hq[0].id, '3bdf30ed8b201f31') # Test various API functions using Austin, TX, USA self.assertEqual(self.api.geo_id(id='c3f37afa9efcf94b').full_name, 'Austin, TX') self.assertTrue(place_name_in_list('Austin, TX', self.api.reverse_geocode(lat=30.267370168467806, long= -97.74261474609375))) # Austin, TX, USA class TweepyCacheTests(unittest.TestCase): timeout = 2.0 memcache_servers = ['127.0.0.1:11211'] # must be running for test to pass def _run_tests(self, do_cleanup=True): # test store and get self.cache.store('testkey', 'testvalue') self.assertEqual(self.cache.get('testkey'), 'testvalue', 'Stored value does not match retrieved value') # test timeout sleep(self.timeout) self.assertEqual(self.cache.get('testkey'), None, 'Cache entry should have expired') # test cleanup if do_cleanup: self.cache.store('testkey', 'testvalue') sleep(self.timeout) self.cache.cleanup() self.assertEqual(self.cache.count(), 0, 'Cache cleanup failed') # test count for i in range(0, 20): self.cache.store('testkey%i' % i, 'testvalue') self.assertEqual(self.cache.count(), 20, 'Count is wrong') # test flush self.cache.flush() self.assertEqual(self.cache.count(), 0, 'Cache failed to flush') def testmemorycache(self): self.cache = MemoryCache(timeout=self.timeout) self._run_tests() def testfilecache(self): os.mkdir('cache_test_dir') self.cache = FileCache('cache_test_dir', self.timeout) self._run_tests() self.cache.flush() os.rmdir('cache_test_dir') if __name__ == '__main__': unittest.main() tweepy-2.1/tests/test_auth.py000066400000000000000000000013001215741226500164040ustar00rootroot00000000000000import unittest from config import * from tweepy import API, OAuthHandler class TweepyAuthTests(unittest.TestCase): def testoauth(self): auth = OAuthHandler(oauth_consumer_key, oauth_consumer_secret) # test getting access token auth_url = auth.get_authorization_url() print 'Please authorize: ' + auth_url verifier = raw_input('PIN: ').strip() self.assert_(len(verifier) > 0) access_token = auth.get_access_token(verifier) self.assert_(access_token is not None) # build api object test using oauth api = API(auth) s = api.update_status('test %i' % random.randint(0, 1000)) api.destroy_status(s.id) tweepy-2.1/tests/test_cursors.py000066400000000000000000000017041215741226500171530ustar00rootroot00000000000000import unittest from tweepy import API, Cursor from config import create_auth class TweepyCursorTests(unittest.TestCase): def setUp(self): self.api = API(create_auth()) def testidcursoritems(self): items = list(Cursor(self.api.user_timeline).items(25)) self.assertEqual(len(items), 25) def testidcursorpages(self): pages = list(Cursor(self.api.user_timeline).pages(5)) self.assertEqual(len(pages), 5) def testcursorcursoritems(self): items = list(Cursor(self.api.friends_ids).items(10)) self.assertEqual(len(items), 10) items = list(Cursor(self.api.followers_ids, 'twitter').items(10)) self.assertEqual(len(items), 10) def testcursorcursorpages(self): pages = list(Cursor(self.api.friends_ids).pages(1)) self.assert_(len(pages) == 1) pages = list(Cursor(self.api.followers_ids, 'twitter').pages(1)) self.assert_(len(pages) == 1) tweepy-2.1/tests/test_resultset.py000066400000000000000000000012461215741226500175060ustar00rootroot00000000000000import unittest from tweepy.models import ResultSet class NoIdItem(object): pass class IdItem(object): def __init__(self, id): self.id = id ids_fixture = [1, 10, 8, 50, 2, 100, 5] class TweepyResultSetTests(unittest.TestCase): def setUp(self): self.results = ResultSet() for i in ids_fixture: self.results.append(IdItem(i)) self.results.append(NoIdItem()) def testids(self): ids = self.results.ids() self.assertListEqual(ids, ids_fixture) def testmaxid(self): self.assertEqual(self.results.max_id, 100) def testsinceid(self): self.assertEqual(self.results.since_id, 1) tweepy-2.1/tests/test_streaming.py000066400000000000000000000036511215741226500174470ustar00rootroot00000000000000from time import sleep import unittest from tweepy.api import API from tweepy.models import Status from tweepy.streaming import Stream, StreamListener from config import create_auth from mock import mock_tweet class MockStreamListener(StreamListener): def __init__(self, test_case): super(MockStreamListener, self).__init__() self.test_case = test_case self.status_count = 0 self.status_stop_count = 0 self.connect_cb = None def on_connect(self): if self.connect_cb: self.connect_cb() def on_timeout(self): self.test_case.fail('timeout') return False def on_status(self, status): self.status_count += 1 self.test_case.assertIsInstance(status, Status) if self.status_stop_count == self.status_count: return False class TweepyStreamTests(unittest.TestCase): def setUp(self): self.auth = create_auth() self.listener = MockStreamListener(self) self.stream = Stream(self.auth, self.listener, timeout=3.0) def tearDown(self): self.stream.disconnect() def test_userstream(self): # Generate random tweet which should show up in the stream. def on_connect(): API(self.auth).update_status(mock_tweet()) self.listener.connect_cb = on_connect self.listener.status_stop_count = 1 self.stream.userstream() self.assertEqual(self.listener.status_count, 1) def test_sample(self): self.listener.status_stop_count = 10 self.stream.sample() self.assertEquals(self.listener.status_count, self.listener.status_stop_count) def test_filter_track(self): self.listener.status_stop_count = 5 phrases = ['twitter'] self.stream.filter(track=phrases) self.assertEquals(self.listener.status_count, self.listener.status_stop_count) tweepy-2.1/tweepy/000077500000000000000000000000001215741226500142135ustar00rootroot00000000000000tweepy-2.1/tweepy/__init__.py000066400000000000000000000013141215741226500163230ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. """ Tweepy Twitter API library """ __version__ = '2.1' __author__ = 'Joshua Roesslein' __license__ = 'MIT' from tweepy.models import Status, User, DirectMessage, Friendship, SavedSearch, SearchResults, ModelFactory, Category from tweepy.error import TweepError from tweepy.api import API from tweepy.cache import Cache, MemoryCache, FileCache from tweepy.auth import BasicAuthHandler, OAuthHandler from tweepy.streaming import Stream, StreamListener from tweepy.cursor import Cursor # Global, unauthenticated instance of API api = API() def debug(enable=True, level=1): import httplib httplib.HTTPConnection.debuglevel = level tweepy-2.1/tweepy/api.py000066400000000000000000000534351215741226500153500ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. import os import mimetypes from tweepy.binder import bind_api from tweepy.error import TweepError from tweepy.parsers import ModelParser from tweepy.utils import list_to_csv class API(object): """Twitter API""" def __init__(self, auth_handler=None, host='api.twitter.com', search_host='search.twitter.com', cache=None, secure=True, api_root='/1.1', search_root='', retry_count=0, retry_delay=0, retry_errors=None, timeout=60, parser=None, compression=False): self.auth = auth_handler self.host = host self.search_host = search_host self.api_root = api_root self.search_root = search_root self.cache = cache self.secure = secure self.compression = compression self.retry_count = retry_count self.retry_delay = retry_delay self.retry_errors = retry_errors self.timeout = timeout self.parser = parser or ModelParser() """ statuses/home_timeline """ home_timeline = bind_api( path = '/statuses/home_timeline.json', payload_type = 'status', payload_list = True, allowed_param = ['since_id', 'max_id', 'count'], require_auth = True ) """ statuses/user_timeline """ user_timeline = bind_api( path = '/statuses/user_timeline.json', payload_type = 'status', payload_list = True, allowed_param = ['id', 'user_id', 'screen_name', 'since_id', 'max_id', 'count', 'include_rts'] ) """ statuses/mentions """ mentions_timeline = bind_api( path = '/statuses/mentions_timeline.json', payload_type = 'status', payload_list = True, allowed_param = ['since_id', 'max_id', 'count'], require_auth = True ) """/statuses/:id/retweeted_by.format""" retweeted_by = bind_api( path = '/statuses/{id}/retweeted_by.json', payload_type = 'status', payload_list = True, allowed_param = ['id', 'count', 'page'], require_auth = True ) """/related_results/show/:id.format""" related_results = bind_api( path = '/related_results/show/{id}.json', payload_type = 'relation', payload_list = True, allowed_param = ['id'], require_auth = False ) """/statuses/:id/retweeted_by/ids.format""" retweeted_by_ids = bind_api( path = '/statuses/{id}/retweeted_by/ids.json', payload_type = 'ids', allowed_param = ['id', 'count', 'page'], require_auth = True ) """ statuses/retweets_of_me """ retweets_of_me = bind_api( path = '/statuses/retweets_of_me.json', payload_type = 'status', payload_list = True, allowed_param = ['since_id', 'max_id', 'count'], require_auth = True ) """ statuses/show """ get_status = bind_api( path = '/statuses/show.json', payload_type = 'status', allowed_param = ['id'] ) """ statuses/update """ update_status = bind_api( path = '/statuses/update.json', method = 'POST', payload_type = 'status', allowed_param = ['status', 'in_reply_to_status_id', 'lat', 'long', 'source', 'place_id'], require_auth = True ) """ statuses/destroy """ destroy_status = bind_api( path = '/statuses/destroy/{id}.json', method = 'POST', payload_type = 'status', allowed_param = ['id'], require_auth = True ) """ statuses/retweet """ retweet = bind_api( path = '/statuses/retweet/{id}.json', method = 'POST', payload_type = 'status', allowed_param = ['id'], require_auth = True ) """ statuses/retweets """ retweets = bind_api( path = '/statuses/retweets/{id}.json', payload_type = 'status', payload_list = True, allowed_param = ['id', 'count'], require_auth = True ) """ users/show """ get_user = bind_api( path = '/users/show.json', payload_type = 'user', allowed_param = ['id', 'user_id', 'screen_name'] ) ''' statuses/oembed ''' get_oembed = bind_api( path = '/statuses/oembed.json', payload_type = 'json', allowed_param = ['id', 'url', 'maxwidth', 'hide_media', 'omit_script', 'align', 'related', 'lang'] ) """ Perform bulk look up of users from user ID or screenname """ def lookup_users(self, user_ids=None, screen_names=None): return self._lookup_users(list_to_csv(user_ids), list_to_csv(screen_names)) _lookup_users = bind_api( path = '/users/lookup.json', payload_type = 'user', payload_list = True, allowed_param = ['user_id', 'screen_name'], ) """ Get the authenticated user """ def me(self): return self.get_user(screen_name=self.auth.get_username()) """ users/search """ search_users = bind_api( path = '/users/search.json', payload_type = 'user', payload_list = True, require_auth = True, allowed_param = ['q', 'per_page', 'page'] ) """ users/suggestions/:slug """ suggested_users = bind_api( path = '/users/suggestions/{slug}.json', payload_type = 'user', payload_list = True, require_auth = True, allowed_param = ['slug', 'lang'] ) """ users/suggestions """ suggested_categories = bind_api( path = '/users/suggestions.json', payload_type = 'category', payload_list = True, allowed_param = ['lang'], require_auth = True ) """ users/suggestions/:slug/members """ suggested_users_tweets = bind_api( path = '/users/suggestions/{slug}/members.json', payload_type = 'status', payload_list = True, allowed_param = ['slug'], require_auth = True ) """ direct_messages """ direct_messages = bind_api( path = '/direct_messages.json', payload_type = 'direct_message', payload_list = True, allowed_param = ['since_id', 'max_id', 'count'], require_auth = True ) """ direct_messages/show """ get_direct_message = bind_api( path = '/direct_messages/show/{id}.json', payload_type = 'direct_message', allowed_param = ['id'], require_auth = True ) """ direct_messages/sent """ sent_direct_messages = bind_api( path = '/direct_messages/sent.json', payload_type = 'direct_message', payload_list = True, allowed_param = ['since_id', 'max_id', 'count', 'page'], require_auth = True ) """ direct_messages/new """ send_direct_message = bind_api( path = '/direct_messages/new.json', method = 'POST', payload_type = 'direct_message', allowed_param = ['user', 'screen_name', 'user_id', 'text'], require_auth = True ) """ direct_messages/destroy """ destroy_direct_message = bind_api( path = '/direct_messages/destroy.json', method = 'DELETE', payload_type = 'direct_message', allowed_param = ['id'], require_auth = True ) """ friendships/create """ create_friendship = bind_api( path = '/friendships/create.json', method = 'POST', payload_type = 'user', allowed_param = ['id', 'user_id', 'screen_name', 'follow'], require_auth = True ) """ friendships/destroy """ destroy_friendship = bind_api( path = '/friendships/destroy.json', method = 'DELETE', payload_type = 'user', allowed_param = ['id', 'user_id', 'screen_name'], require_auth = True ) """ friendships/show """ show_friendship = bind_api( path = '/friendships/show.json', payload_type = 'friendship', allowed_param = ['source_id', 'source_screen_name', 'target_id', 'target_screen_name'] ) """ Perform bulk look up of friendships from user ID or screenname """ def lookup_friendships(self, user_ids=None, screen_names=None): return self._lookup_friendships(list_to_csv(user_ids), list_to_csv(screen_names)) _lookup_friendships = bind_api( path = '/friendships/lookup.json', payload_type = 'relationship', payload_list = True, allowed_param = ['user_id', 'screen_name'], require_auth = True ) """ friends/ids """ friends_ids = bind_api( path = '/friends/ids.json', payload_type = 'ids', allowed_param = ['id', 'user_id', 'screen_name', 'cursor'] ) """ friends/list """ friends = bind_api( path = '/friends/list.json', payload_type = 'user', payload_list = True, allowed_param = ['id', 'user_id', 'screen_name', 'cursor'] ) """ friendships/incoming """ friendships_incoming = bind_api( path = '/friendships/incoming.json', payload_type = 'ids', allowed_param = ['cursor'] ) """ friendships/outgoing""" friendships_outgoing = bind_api( path = '/friendships/outgoing.json', payload_type = 'ids', allowed_param = ['cursor'] ) """ followers/ids """ followers_ids = bind_api( path = '/followers/ids.json', payload_type = 'ids', allowed_param = ['id', 'user_id', 'screen_name', 'cursor'] ) """ followers/list """ followers = bind_api( path = '/followers/list.json', payload_type = 'user', payload_list = True, allowed_param = ['id', 'user_id', 'screen_name', 'cursor'] ) """ account/verify_credentials """ def verify_credentials(self, **kargs): try: return bind_api( path = '/account/verify_credentials.json', payload_type = 'user', require_auth = True, allowed_param = ['include_entities', 'skip_status'], )(self, **kargs) except TweepError, e: if e.response and e.response.status == 401: return False raise """ account/rate_limit_status """ rate_limit_status = bind_api( path = '/application/rate_limit_status.json', payload_type = 'json', allowed_param = ['resources'], use_cache = False ) """ account/update_delivery_device """ set_delivery_device = bind_api( path = '/account/update_delivery_device.json', method = 'POST', allowed_param = ['device'], payload_type = 'user', require_auth = True ) """ account/update_profile_colors """ update_profile_colors = bind_api( path = '/account/update_profile_colors.json', method = 'POST', payload_type = 'user', allowed_param = ['profile_background_color', 'profile_text_color', 'profile_link_color', 'profile_sidebar_fill_color', 'profile_sidebar_border_color'], require_auth = True ) """ account/update_profile_image """ def update_profile_image(self, filename): headers, post_data = API._pack_image(filename, 700) return bind_api( path = '/account/update_profile_image.json', method = 'POST', payload_type = 'user', require_auth = True )(self, post_data=post_data, headers=headers) """ account/update_profile_background_image """ def update_profile_background_image(self, filename, *args, **kargs): headers, post_data = API._pack_image(filename, 800) bind_api( path = '/account/update_profile_background_image.json', method = 'POST', payload_type = 'user', allowed_param = ['tile'], require_auth = True )(self, post_data=post_data, headers=headers) """ account/update_profile """ update_profile = bind_api( path = '/account/update_profile.json', method = 'POST', payload_type = 'user', allowed_param = ['name', 'url', 'location', 'description'], require_auth = True ) """ favorites """ favorites = bind_api( path = '/favorites/list.json', payload_type = 'status', payload_list = True, allowed_param = ['screen_name', 'user_id', 'max_id', 'count', 'since_id', 'max_id'] ) """ favorites/create """ create_favorite = bind_api( path = '/favorites/create.json', method = 'POST', payload_type = 'status', allowed_param = ['id'], require_auth = True ) """ favorites/destroy """ destroy_favorite = bind_api( path = '/favorites/destroy.json', method = 'POST', payload_type = 'status', allowed_param = ['id'], require_auth = True ) """ blocks/create """ create_block = bind_api( path = '/blocks/create.json', method = 'POST', payload_type = 'user', allowed_param = ['id', 'user_id', 'screen_name'], require_auth = True ) """ blocks/destroy """ destroy_block = bind_api( path = '/blocks/destroy.json', method = 'DELETE', payload_type = 'user', allowed_param = ['id', 'user_id', 'screen_name'], require_auth = True ) """ blocks/blocking """ blocks = bind_api( path = '/blocks/list.json', payload_type = 'user', payload_list = True, allowed_param = ['cursor'], require_auth = True ) """ blocks/blocking/ids """ blocks_ids = bind_api( path = '/blocks/ids.json', payload_type = 'json', require_auth = True ) """ report_spam """ report_spam = bind_api( path = '/users/report_spam.json', method = 'POST', payload_type = 'user', allowed_param = ['user_id', 'screen_name'], require_auth = True ) """ saved_searches """ saved_searches = bind_api( path = '/saved_searches/list.json', payload_type = 'saved_search', payload_list = True, require_auth = True ) """ saved_searches/show """ get_saved_search = bind_api( path = '/saved_searches/show/{id}.json', payload_type = 'saved_search', allowed_param = ['id'], require_auth = True ) """ saved_searches/create """ create_saved_search = bind_api( path = '/saved_searches/create.json', method = 'POST', payload_type = 'saved_search', allowed_param = ['query'], require_auth = True ) """ saved_searches/destroy """ destroy_saved_search = bind_api( path = '/saved_searches/destroy/{id}.json', method = 'POST', payload_type = 'saved_search', allowed_param = ['id'], require_auth = True ) """ help/test """ def test(self): try: bind_api( path = '/help/test.json', )(self) except TweepError: return False return True create_list = bind_api( path = '/lists/create.json', method = 'POST', payload_type = 'list', allowed_param = ['name', 'mode', 'description'], require_auth = True ) destroy_list = bind_api( path = '/lists/destroy.json', method = 'POST', payload_type = 'list', allowed_param = ['owner_screen_name', 'owner_id', 'list_id', 'slug'], require_auth = True ) update_list = bind_api( path = '/lists/update.json', method = 'POST', payload_type = 'list', allowed_param = ['list_id', 'slug', 'name', 'mode', 'description', 'owner_screen_name', 'owner_id'], require_auth = True ) lists_all = bind_api( path = '/lists/list.json', payload_type = 'list', payload_list = True, allowed_param = ['screen_name', 'user_id'], require_auth = True ) lists_memberships = bind_api( path = '/lists/memberships.json', payload_type = 'list', payload_list = True, allowed_param = ['screen_name', 'user_id', 'filter_to_owned_lists', 'cursor'], require_auth = True ) lists_subscriptions = bind_api( path = '/lists/subscriptions.json', payload_type = 'list', payload_list = True, allowed_param = ['screen_name', 'user_id', 'cursor'], require_auth = True ) list_timeline = bind_api( path = '/lists/statuses.json', payload_type = 'status', payload_list = True, allowed_param = ['owner_screen_name', 'slug', 'owner_id', 'list_id', 'since_id', 'max_id', 'count'] ) get_list = bind_api( path = '/lists/show.json', payload_type = 'list', allowed_param = ['owner_screen_name', 'owner_id', 'slug', 'list_id'] ) add_list_member = bind_api( path = '/lists/members/create.json', method = 'POST', payload_type = 'list', allowed_param = ['screen_name', 'user_id', 'owner_screen_name', 'owner_id', 'slug', 'list_id'], require_auth = True ) remove_list_member = bind_api( path = '/lists/members/destroy.json', method = 'POST', payload_type = 'list', allowed_param = ['screen_name', 'user_id', 'owner_screen_name', 'owner_id', 'slug', 'list_id'], require_auth = True ) list_members = bind_api( path = '/lists/members.json', payload_type = 'user', payload_list = True, allowed_param = ['owner_screen_name', 'slug', 'list_id', 'owner_id', 'cursor'] ) show_list_member = bind_api( path = '/lists/members/show.json', payload_type = 'user', allowed_param = ['list_id', 'slug', 'user_id', 'screen_name', 'owner_screen_name', 'owner_id'] ) subscribe_list = bind_api( path = '/lists/subscribers/create.json', method = 'POST', payload_type = 'list', allowed_param = ['owner_screen_name', 'slug', 'owner_id', 'list_id'], require_auth = True ) unsubscribe_list = bind_api( path = '/lists/subscribers/destroy.json', method = 'POST', payload_type = 'list', allowed_param = ['owner_screen_name', 'slug', 'owner_id', 'list_id'], require_auth = True ) list_subscribers = bind_api( path = '/lists/subscribers.json', payload_type = 'user', payload_list = True, allowed_param = ['owner_screen_name', 'slug', 'owner_id', 'list_id', 'cursor'] ) show_list_subscriber = bind_api( path = '/lists/subscribers/show.json', payload_type = 'user', allowed_param = ['owner_screen_name', 'slug', 'screen_name', 'owner_id', 'list_id', 'user_id'] ) """ trends/available """ trends_available = bind_api( path = '/trends/available.json', payload_type = 'json' ) trends_place = bind_api( path = '/trends/place.json', payload_type = 'json', allowed_param = ['id', 'exclude'] ) trends_closest = bind_api( path = '/trends/closest.json', payload_type = 'json', allowed_param = ['lat', 'long'] ) """ search """ search = bind_api( path = '/search/tweets.json', payload_type = 'search_results', allowed_param = ['q', 'lang', 'locale', 'since_id', 'geocode', 'show_user', 'max_id', 'since', 'until', 'result_type'] ) """ trends/daily """ trends_daily = bind_api( path = '/trends/daily.json', payload_type = 'json', allowed_param = ['date', 'exclude'] ) """ trends/weekly """ trends_weekly = bind_api( path = '/trends/weekly.json', payload_type = 'json', allowed_param = ['date', 'exclude'] ) """ geo/reverse_geocode """ reverse_geocode = bind_api( path = '/geo/reverse_geocode.json', payload_type = 'place', payload_list = True, allowed_param = ['lat', 'long', 'accuracy', 'granularity', 'max_results'] ) """ geo/id """ geo_id = bind_api( path = '/geo/id/{id}.json', payload_type = 'place', allowed_param = ['id'] ) """ geo/search """ geo_search = bind_api( path = '/geo/search.json', payload_type = 'place', payload_list = True, allowed_param = ['lat', 'long', 'query', 'ip', 'granularity', 'accuracy', 'max_results', 'contained_within'] ) """ geo/similar_places """ geo_similar_places = bind_api( path = '/geo/similar_places.json', payload_type = 'place', payload_list = True, allowed_param = ['lat', 'long', 'name', 'contained_within'] ) """ Internal use only """ @staticmethod def _pack_image(filename, max_size): """Pack image from file into multipart-formdata post body""" # image must be less than 700kb in size try: if os.path.getsize(filename) > (max_size * 1024): raise TweepError('File is too big, must be less than 700kb.') except os.error: raise TweepError('Unable to access file') # image must be gif, jpeg, or png file_type = mimetypes.guess_type(filename) if file_type is None: raise TweepError('Could not determine file type') file_type = file_type[0] if file_type not in ['image/gif', 'image/jpeg', 'image/png']: raise TweepError('Invalid file type for image: %s' % file_type) # build the mulitpart-formdata body fp = open(filename, 'rb') BOUNDARY = 'Tw3ePy' body = [] body.append('--' + BOUNDARY) body.append('Content-Disposition: form-data; name="image"; filename="%s"' % filename) body.append('Content-Type: %s' % file_type) body.append('') body.append(fp.read()) body.append('--' + BOUNDARY + '--') body.append('') fp.close() body = '\r\n'.join(body) # build headers headers = { 'Content-Type': 'multipart/form-data; boundary=Tw3ePy', 'Content-Length': str(len(body)) } return headers, body tweepy-2.1/tweepy/auth.py000066400000000000000000000131431215741226500155300ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. from urllib2 import Request, urlopen import base64 from tweepy import oauth from tweepy.error import TweepError from tweepy.api import API class AuthHandler(object): def apply_auth(self, url, method, headers, parameters): """Apply authentication headers to request""" raise NotImplementedError def get_username(self): """Return the username of the authenticated user""" raise NotImplementedError class BasicAuthHandler(AuthHandler): def __init__(self, username, password): self.username = username self._b64up = base64.b64encode('%s:%s' % (username, password)) def apply_auth(self, url, method, headers, parameters): headers['Authorization'] = 'Basic %s' % self._b64up def get_username(self): return self.username class OAuthHandler(AuthHandler): """OAuth authentication handler""" OAUTH_HOST = 'api.twitter.com' OAUTH_ROOT = '/oauth/' def __init__(self, consumer_key, consumer_secret, callback=None, secure=False): self._consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) self._sigmethod = oauth.OAuthSignatureMethod_HMAC_SHA1() self.request_token = None self.access_token = None self.callback = callback self.username = None self.secure = secure def _get_oauth_url(self, endpoint, secure=False): if self.secure or secure: prefix = 'https://' else: prefix = 'http://' return prefix + self.OAUTH_HOST + self.OAUTH_ROOT + endpoint def apply_auth(self, url, method, headers, parameters): request = oauth.OAuthRequest.from_consumer_and_token( self._consumer, http_url=url, http_method=method, token=self.access_token, parameters=parameters ) request.sign_request(self._sigmethod, self._consumer, self.access_token) headers.update(request.to_header()) def _get_request_token(self): try: url = self._get_oauth_url('request_token') request = oauth.OAuthRequest.from_consumer_and_token( self._consumer, http_url=url, callback=self.callback ) request.sign_request(self._sigmethod, self._consumer, None) resp = urlopen(Request(url, headers=request.to_header())) return oauth.OAuthToken.from_string(resp.read()) except Exception, e: raise TweepError(e) def set_request_token(self, key, secret): self.request_token = oauth.OAuthToken(key, secret) def set_access_token(self, key, secret): self.access_token = oauth.OAuthToken(key, secret) def get_authorization_url(self, signin_with_twitter=False): """Get the authorization URL to redirect the user""" try: # get the request token self.request_token = self._get_request_token() # build auth request and return as url if signin_with_twitter: url = self._get_oauth_url('authenticate') else: url = self._get_oauth_url('authorize') request = oauth.OAuthRequest.from_token_and_callback( token=self.request_token, http_url=url ) return request.to_url() except Exception, e: raise TweepError(e) def get_access_token(self, verifier=None): """ After user has authorized the request token, get access token with user supplied verifier. """ try: url = self._get_oauth_url('access_token') # build request request = oauth.OAuthRequest.from_consumer_and_token( self._consumer, token=self.request_token, http_url=url, verifier=str(verifier) ) request.sign_request(self._sigmethod, self._consumer, self.request_token) # send request resp = urlopen(Request(url, headers=request.to_header())) self.access_token = oauth.OAuthToken.from_string(resp.read()) return self.access_token except Exception, e: raise TweepError(e) def get_xauth_access_token(self, username, password): """ Get an access token from an username and password combination. In order to get this working you need to create an app at http://twitter.com/apps, after that send a mail to api@twitter.com and request activation of xAuth for it. """ try: url = self._get_oauth_url('access_token', secure=True) # must use HTTPS request = oauth.OAuthRequest.from_consumer_and_token( oauth_consumer=self._consumer, http_method='POST', http_url=url, parameters = { 'x_auth_mode': 'client_auth', 'x_auth_username': username, 'x_auth_password': password } ) request.sign_request(self._sigmethod, self._consumer, None) resp = urlopen(Request(url, data=request.to_postdata())) self.access_token = oauth.OAuthToken.from_string(resp.read()) return self.access_token except Exception, e: raise TweepError(e) def get_username(self): if self.username is None: api = API(self) user = api.verify_credentials() if user: self.username = user.screen_name else: raise TweepError("Unable to get username, invalid oauth token!") return self.username tweepy-2.1/tweepy/binder.py000066400000000000000000000172531215741226500160400ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. import httplib import urllib import time import re from StringIO import StringIO import gzip from tweepy.error import TweepError from tweepy.utils import convert_to_utf8_str from tweepy.models import Model re_path_template = re.compile('{\w+}') def bind_api(**config): class APIMethod(object): path = config['path'] payload_type = config.get('payload_type', None) payload_list = config.get('payload_list', False) allowed_param = config.get('allowed_param', []) method = config.get('method', 'GET') require_auth = config.get('require_auth', False) search_api = config.get('search_api', False) use_cache = config.get('use_cache', True) def __init__(self, api, args, kargs): # If authentication is required and no credentials # are provided, throw an error. if self.require_auth and not api.auth: raise TweepError('Authentication required!') self.api = api self.post_data = kargs.pop('post_data', None) self.retry_count = kargs.pop('retry_count', api.retry_count) self.retry_delay = kargs.pop('retry_delay', api.retry_delay) self.retry_errors = kargs.pop('retry_errors', api.retry_errors) self.headers = kargs.pop('headers', {}) self.build_parameters(args, kargs) # Pick correct URL root to use if self.search_api: self.api_root = api.search_root else: self.api_root = api.api_root # Perform any path variable substitution self.build_path() if api.secure: self.scheme = 'https://' else: self.scheme = 'http://' if self.search_api: self.host = api.search_host else: self.host = api.host # Manually set Host header to fix an issue in python 2.5 # or older where Host is set including the 443 port. # This causes Twitter to issue 301 redirect. # See Issue https://github.com/tweepy/tweepy/issues/12 self.headers['Host'] = self.host def build_parameters(self, args, kargs): self.parameters = {} for idx, arg in enumerate(args): if arg is None: continue try: self.parameters[self.allowed_param[idx]] = convert_to_utf8_str(arg) except IndexError: raise TweepError('Too many parameters supplied!') for k, arg in kargs.items(): if arg is None: continue if k in self.parameters: raise TweepError('Multiple values for parameter %s supplied!' % k) self.parameters[k] = convert_to_utf8_str(arg) def build_path(self): for variable in re_path_template.findall(self.path): name = variable.strip('{}') if name == 'user' and 'user' not in self.parameters and self.api.auth: # No 'user' parameter provided, fetch it from Auth instead. value = self.api.auth.get_username() else: try: value = urllib.quote(self.parameters[name]) except KeyError: raise TweepError('No parameter value found for path variable: %s' % name) del self.parameters[name] self.path = self.path.replace(variable, value) def execute(self): # Build the request URL url = self.api_root + self.path if len(self.parameters): url = '%s?%s' % (url, urllib.urlencode(self.parameters)) # Query the cache if one is available # and this request uses a GET method. if self.use_cache and self.api.cache and self.method == 'GET': cache_result = self.api.cache.get(url) # if cache result found and not expired, return it if cache_result: # must restore api reference if isinstance(cache_result, list): for result in cache_result: if isinstance(result, Model): result._api = self.api else: if isinstance(cache_result, Model): cache_result._api = self.api return cache_result # Continue attempting request until successful # or maximum number of retries is reached. retries_performed = 0 while retries_performed < self.retry_count + 1: # Open connection if self.api.secure: conn = httplib.HTTPSConnection(self.host, timeout=self.api.timeout) else: conn = httplib.HTTPConnection(self.host, timeout=self.api.timeout) # Apply authentication if self.api.auth: self.api.auth.apply_auth( self.scheme + self.host + url, self.method, self.headers, self.parameters ) # Request compression if configured if self.api.compression: self.headers['Accept-encoding'] = 'gzip' # Execute request try: conn.request(self.method, url, headers=self.headers, body=self.post_data) resp = conn.getresponse() except Exception, e: raise TweepError('Failed to send request: %s' % e) # Exit request loop if non-retry error code if self.retry_errors: if resp.status not in self.retry_errors: break else: if resp.status == 200: break # Sleep before retrying request again time.sleep(self.retry_delay) retries_performed += 1 # If an error was returned, throw an exception self.api.last_response = resp if resp.status != 200: try: error_msg = self.api.parser.parse_error(resp.read()) except Exception: error_msg = "Twitter error response: status code = %s" % resp.status raise TweepError(error_msg, resp) # Parse the response payload body = resp.read() if resp.getheader('Content-Encoding', '') == 'gzip': try: zipper = gzip.GzipFile(fileobj=StringIO(body)) body = zipper.read() except Exception, e: raise TweepError('Failed to decompress data: %s' % e) result = self.api.parser.parse(self, body) conn.close() # Store result into cache if one is available. if self.use_cache and self.api.cache and self.method == 'GET' and result: self.api.cache.store(url, result) return result def _call(api, *args, **kargs): method = APIMethod(api, args, kargs) return method.execute() # Set pagination mode if 'cursor' in APIMethod.allowed_param: _call.pagination_mode = 'cursor' elif 'max_id' in APIMethod.allowed_param and \ 'since_id' in APIMethod.allowed_param: _call.pagination_mode = 'id' elif 'page' in APIMethod.allowed_param: _call.pagination_mode = 'page' return _call tweepy-2.1/tweepy/cache.py000066400000000000000000000307041215741226500156340ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. import time import datetime import threading import os try: import cPickle as pickle except ImportError: import pickle try: import hashlib except ImportError: # python 2.4 import md5 as hashlib try: import fcntl except ImportError: # Probably on a windows system # TODO: use win32file pass class Cache(object): """Cache interface""" def __init__(self, timeout=60): """Initialize the cache timeout: number of seconds to keep a cached entry """ self.timeout = timeout def store(self, key, value): """Add new record to cache key: entry key value: data of entry """ raise NotImplementedError def get(self, key, timeout=None): """Get cached entry if exists and not expired key: which entry to get timeout: override timeout with this value [optional] """ raise NotImplementedError def count(self): """Get count of entries currently stored in cache""" raise NotImplementedError def cleanup(self): """Delete any expired entries in cache.""" raise NotImplementedError def flush(self): """Delete all cached entries""" raise NotImplementedError class MemoryCache(Cache): """In-memory cache""" def __init__(self, timeout=60): Cache.__init__(self, timeout) self._entries = {} self.lock = threading.Lock() def __getstate__(self): # pickle return {'entries': self._entries, 'timeout': self.timeout} def __setstate__(self, state): # unpickle self.lock = threading.Lock() self._entries = state['entries'] self.timeout = state['timeout'] def _is_expired(self, entry, timeout): return timeout > 0 and (time.time() - entry[0]) >= timeout def store(self, key, value): self.lock.acquire() self._entries[key] = (time.time(), value) self.lock.release() def get(self, key, timeout=None): self.lock.acquire() try: # check to see if we have this key entry = self._entries.get(key) if not entry: # no hit, return nothing return None # use provided timeout in arguments if provided # otherwise use the one provided during init. if timeout is None: timeout = self.timeout # make sure entry is not expired if self._is_expired(entry, timeout): # entry expired, delete and return nothing del self._entries[key] return None # entry found and not expired, return it return entry[1] finally: self.lock.release() def count(self): return len(self._entries) def cleanup(self): self.lock.acquire() try: for k, v in self._entries.items(): if self._is_expired(v, self.timeout): del self._entries[k] finally: self.lock.release() def flush(self): self.lock.acquire() self._entries.clear() self.lock.release() class FileCache(Cache): """File-based cache""" # locks used to make cache thread-safe cache_locks = {} def __init__(self, cache_dir, timeout=60): Cache.__init__(self, timeout) if os.path.exists(cache_dir) is False: os.mkdir(cache_dir) self.cache_dir = cache_dir if cache_dir in FileCache.cache_locks: self.lock = FileCache.cache_locks[cache_dir] else: self.lock = threading.Lock() FileCache.cache_locks[cache_dir] = self.lock if os.name == 'posix': self._lock_file = self._lock_file_posix self._unlock_file = self._unlock_file_posix elif os.name == 'nt': self._lock_file = self._lock_file_win32 self._unlock_file = self._unlock_file_win32 else: print 'Warning! FileCache locking not supported on this system!' self._lock_file = self._lock_file_dummy self._unlock_file = self._unlock_file_dummy def _get_path(self, key): md5 = hashlib.md5() md5.update(key) return os.path.join(self.cache_dir, md5.hexdigest()) def _lock_file_dummy(self, path, exclusive=True): return None def _unlock_file_dummy(self, lock): return def _lock_file_posix(self, path, exclusive=True): lock_path = path + '.lock' if exclusive is True: f_lock = open(lock_path, 'w') fcntl.lockf(f_lock, fcntl.LOCK_EX) else: f_lock = open(lock_path, 'r') fcntl.lockf(f_lock, fcntl.LOCK_SH) if os.path.exists(lock_path) is False: f_lock.close() return None return f_lock def _unlock_file_posix(self, lock): lock.close() def _lock_file_win32(self, path, exclusive=True): # TODO: implement return None def _unlock_file_win32(self, lock): # TODO: implement return def _delete_file(self, path): os.remove(path) if os.path.exists(path + '.lock'): os.remove(path + '.lock') def store(self, key, value): path = self._get_path(key) self.lock.acquire() try: # acquire lock and open file f_lock = self._lock_file(path) datafile = open(path, 'wb') # write data pickle.dump((time.time(), value), datafile) # close and unlock file datafile.close() self._unlock_file(f_lock) finally: self.lock.release() def get(self, key, timeout=None): return self._get(self._get_path(key), timeout) def _get(self, path, timeout): if os.path.exists(path) is False: # no record return None self.lock.acquire() try: # acquire lock and open f_lock = self._lock_file(path, False) datafile = open(path, 'rb') # read pickled object created_time, value = pickle.load(datafile) datafile.close() # check if value is expired if timeout is None: timeout = self.timeout if timeout > 0 and (time.time() - created_time) >= timeout: # expired! delete from cache value = None self._delete_file(path) # unlock and return result self._unlock_file(f_lock) return value finally: self.lock.release() def count(self): c = 0 for entry in os.listdir(self.cache_dir): if entry.endswith('.lock'): continue c += 1 return c def cleanup(self): for entry in os.listdir(self.cache_dir): if entry.endswith('.lock'): continue self._get(os.path.join(self.cache_dir, entry), None) def flush(self): for entry in os.listdir(self.cache_dir): if entry.endswith('.lock'): continue self._delete_file(os.path.join(self.cache_dir, entry)) class MemCacheCache(Cache): """Cache interface""" def __init__(self, client, timeout=60): """Initialize the cache client: The memcache client timeout: number of seconds to keep a cached entry """ self.client = client self.timeout = timeout def store(self, key, value): """Add new record to cache key: entry key value: data of entry """ self.client.set(key, value, time=self.timeout) def get(self, key, timeout=None): """Get cached entry if exists and not expired key: which entry to get timeout: override timeout with this value [optional]. DOES NOT WORK HERE """ return self.client.get(key) def count(self): """Get count of entries currently stored in cache. RETURN 0""" raise NotImplementedError def cleanup(self): """Delete any expired entries in cache. NO-OP""" raise NotImplementedError def flush(self): """Delete all cached entries. NO-OP""" raise NotImplementedError class RedisCache(Cache): '''Cache running in a redis server''' def __init__(self, client, timeout=60, keys_container = 'tweepy:keys', pre_identifier = 'tweepy:'): Cache.__init__(self, timeout) self.client = client self.keys_container = keys_container self.pre_identifier = pre_identifier def _is_expired(self, entry, timeout): # Returns true if the entry has expired return timeout > 0 and (time.time() - entry[0]) >= timeout def store(self, key, value): '''Store the key, value pair in our redis server''' # Prepend tweepy to our key, this makes it easier to identify tweepy keys in our redis server key = self.pre_identifier + key # Get a pipe (to execute several redis commands in one step) pipe = self.client.pipeline() # Set our values in a redis hash (similar to python dict) pipe.set(key, pickle.dumps((time.time(), value))) # Set the expiration pipe.expire(key, self.timeout) # Add the key to a set containing all the keys pipe.sadd(self.keys_container, key) # Execute the instructions in the redis server pipe.execute() def get(self, key, timeout=None): '''Given a key, returns an element from the redis table''' key = self.pre_identifier + key # Check to see if we have this key unpickled_entry = self.client.get(key) if not unpickled_entry: # No hit, return nothing return None entry = pickle.loads(unpickled_entry) # Use provided timeout in arguments if provided # otherwise use the one provided during init. if timeout is None: timeout = self.timeout # Make sure entry is not expired if self._is_expired(entry, timeout): # entry expired, delete and return nothing self.delete_entry(key) return None # entry found and not expired, return it return entry[1] def count(self): '''Note: This is not very efficient, since it retreives all the keys from the redis server to know how many keys we have''' return len(self.client.smembers(self.keys_container)) def delete_entry(self, key): '''Delete an object from the redis table''' pipe = self.client.pipeline() pipe.srem(self.keys_container, key) pipe.delete(key) pipe.execute() def cleanup(self): '''Cleanup all the expired keys''' keys = self.client.smembers(self.keys_container) for key in keys: entry = self.client.get(key) if entry: entry = pickle.loads(entry) if self._is_expired(entry, self.timeout): self.delete_entry(key) def flush(self): '''Delete all entries from the cache''' keys = self.client.smembers(self.keys_container) for key in keys: self.delete_entry(key) class MongodbCache(Cache): """A simple pickle-based MongoDB cache sytem.""" def __init__(self, db, timeout=3600, collection='tweepy_cache'): """Should receive a "database" cursor from pymongo.""" Cache.__init__(self, timeout) self.timeout = timeout self.col = db[collection] self.col.create_index('created', expireAfterSeconds=timeout) def store(self, key, value): from bson.binary import Binary now = datetime.datetime.utcnow() blob = Binary(pickle.dumps(value)) self.col.insert({'created': now, '_id': key, 'value': blob}) def get(self, key, timeout=None): if timeout: raise NotImplementedError obj = self.col.find_one({'_id': key}) if obj: return pickle.loads(obj['value']) def count(self): return self.col.find({}).count() def delete_entry(self, key): return self.col.remove({'_id': key}) def cleanup(self): """MongoDB will automatically clear expired keys.""" pass def flush(self): self.col.drop() self.col.create_index('created', expireAfterSeconds=self.timeout) tweepy-2.1/tweepy/cursor.py000066400000000000000000000126321215741226500161060ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. from tweepy.error import TweepError class Cursor(object): """Pagination helper class""" def __init__(self, method, *args, **kargs): if hasattr(method, 'pagination_mode'): if method.pagination_mode == 'cursor': self.iterator = CursorIterator(method, args, kargs) elif method.pagination_mode == 'id': self.iterator = IdIterator(method, args, kargs) elif method.pagination_mode == 'page': self.iterator = PageIterator(method, args, kargs) else: raise TweepError('Invalid pagination mode.') else: raise TweepError('This method does not perform pagination') def pages(self, limit=0): """Return iterator for pages""" if limit > 0: self.iterator.limit = limit return self.iterator def items(self, limit=0): """Return iterator for items in each page""" i = ItemIterator(self.iterator) i.limit = limit return i class BaseIterator(object): def __init__(self, method, args, kargs): self.method = method self.args = args self.kargs = kargs self.limit = 0 def next(self): raise NotImplementedError def prev(self): raise NotImplementedError def __iter__(self): return self class CursorIterator(BaseIterator): def __init__(self, method, args, kargs): BaseIterator.__init__(self, method, args, kargs) self.next_cursor = -1 self.prev_cursor = 0 self.count = 0 def next(self): if self.next_cursor == 0 or (self.limit and self.count == self.limit): raise StopIteration data, cursors = self.method( cursor=self.next_cursor, *self.args, **self.kargs ) self.prev_cursor, self.next_cursor = cursors if len(data) == 0: raise StopIteration self.count += 1 return data def prev(self): if self.prev_cursor == 0: raise TweepError('Can not page back more, at first page') data, self.next_cursor, self.prev_cursor = self.method( cursor=self.prev_cursor, *self.args, **self.kargs ) self.count -= 1 return data class IdIterator(BaseIterator): def __init__(self, method, args, kargs): BaseIterator.__init__(self, method, args, kargs) self.max_id = kargs.get('max_id') self.since_id = kargs.get('since_id') self.count = 0 def next(self): """Fetch a set of items with IDs less than current set.""" if self.limit and self.limit == self.count: raise StopIteration # max_id is inclusive so decrement by one # to avoid requesting duplicate items. max_id = self.since_id - 1 if self.max_id else None data = self.method(max_id = max_id, *self.args, **self.kargs) if len(data) == 0: raise StopIteration self.max_id = data.max_id self.since_id = data.since_id self.count += 1 return data def prev(self): """Fetch a set of items with IDs greater than current set.""" if self.limit and self.limit == self.count: raise StopIteration since_id = self.max_id data = self.method(since_id = since_id, *self.args, **self.kargs) if len(data) == 0: raise StopIteration self.max_id = data.max_id self.since_id = data.since_id self.count += 1 return data class PageIterator(BaseIterator): def __init__(self, method, args, kargs): BaseIterator.__init__(self, method, args, kargs) self.current_page = 0 def next(self): self.current_page += 1 items = self.method(page=self.current_page, *self.args, **self.kargs) if len(items) == 0 or (self.limit > 0 and self.current_page > self.limit): raise StopIteration return items def prev(self): if (self.current_page == 1): raise TweepError('Can not page back more, at first page') self.current_page -= 1 return self.method(page=self.current_page, *self.args, **self.kargs) class ItemIterator(BaseIterator): def __init__(self, page_iterator): self.page_iterator = page_iterator self.limit = 0 self.current_page = None self.page_index = -1 self.count = 0 def next(self): if self.limit > 0 and self.count == self.limit: raise StopIteration if self.current_page is None or self.page_index == len(self.current_page) - 1: # Reached end of current page, get the next page... self.current_page = self.page_iterator.next() self.page_index = -1 self.page_index += 1 self.count += 1 return self.current_page[self.page_index] def prev(self): if self.current_page is None: raise TweepError('Can not go back more, at first page') if self.page_index == 0: # At the beginning of the current page, move to next... self.current_page = self.page_iterator.prev() self.page_index = len(self.current_page) if self.page_index == 0: raise TweepError('No more items') self.page_index -= 1 self.count -= 1 return self.current_page[self.page_index] tweepy-2.1/tweepy/error.py000066400000000000000000000005301215741226500157140ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. class TweepError(Exception): """Tweepy exception""" def __init__(self, reason, response=None): self.reason = unicode(reason) self.response = response Exception.__init__(self, reason) def __str__(self): return self.reason tweepy-2.1/tweepy/models.py000066400000000000000000000276771215741226500160730ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. from tweepy.error import TweepError from tweepy.utils import parse_datetime, parse_html_value, parse_a_href, \ parse_search_datetime, unescape_html class ResultSet(list): """A list like object that holds results from a Twitter API query.""" def __init__(self, max_id=None, since_id=None): super(ResultSet, self).__init__() self._max_id = max_id self._since_id = since_id @property def max_id(self): if self._max_id: return self._max_id ids = self.ids() return max(ids) if ids else None @property def since_id(self): if self._since_id: return self._since_id ids = self.ids() return min(ids) if ids else None def ids(self): return [item.id for item in self if hasattr(item, 'id')] class Model(object): def __init__(self, api=None): self._api = api def __getstate__(self): # pickle pickle = dict(self.__dict__) try: del pickle['_api'] # do not pickle the API reference except KeyError: pass return pickle @classmethod def parse(cls, api, json): """Parse a JSON object into a model instance.""" raise NotImplementedError @classmethod def parse_list(cls, api, json_list): """Parse a list of JSON objects into a result set of model instances.""" results = ResultSet() for obj in json_list: if obj: results.append(cls.parse(api, obj)) return results class Status(Model): @classmethod def parse(cls, api, json): status = cls(api) for k, v in json.items(): if k == 'user': user_model = getattr(api.parser.model_factory, 'user') user = user_model.parse(api, v) setattr(status, 'author', user) setattr(status, 'user', user) # DEPRECIATED elif k == 'created_at': setattr(status, k, parse_datetime(v)) elif k == 'source': if '<' in v: setattr(status, k, parse_html_value(v)) setattr(status, 'source_url', parse_a_href(v)) else: setattr(status, k, v) setattr(status, 'source_url', None) elif k == 'retweeted_status': setattr(status, k, Status.parse(api, v)) elif k == 'place': if v is not None: setattr(status, k, Place.parse(api, v)) else: setattr(status, k, None) else: setattr(status, k, v) return status def destroy(self): return self._api.destroy_status(self.id) def retweet(self): return self._api.retweet(self.id) def retweets(self): return self._api.retweets(self.id) def favorite(self): return self._api.create_favorite(self.id) class User(Model): @classmethod def parse(cls, api, json): user = cls(api) for k, v in json.items(): if k == 'created_at': setattr(user, k, parse_datetime(v)) elif k == 'status': setattr(user, k, Status.parse(api, v)) elif k == 'following': # twitter sets this to null if it is false if v is True: setattr(user, k, True) else: setattr(user, k, False) else: setattr(user, k, v) return user @classmethod def parse_list(cls, api, json_list): if isinstance(json_list, list): item_list = json_list else: item_list = json_list['users'] results = ResultSet() for obj in item_list: results.append(cls.parse(api, obj)) return results def timeline(self, **kargs): return self._api.user_timeline(user_id=self.id, **kargs) def friends(self, **kargs): return self._api.friends(user_id=self.id, **kargs) def followers(self, **kargs): return self._api.followers(user_id=self.id, **kargs) def follow(self): self._api.create_friendship(user_id=self.id) self.following = True def unfollow(self): self._api.destroy_friendship(user_id=self.id) self.following = False def lists_memberships(self, *args, **kargs): return self._api.lists_memberships(user=self.screen_name, *args, **kargs) def lists_subscriptions(self, *args, **kargs): return self._api.lists_subscriptions(user=self.screen_name, *args, **kargs) def lists(self, *args, **kargs): return self._api.lists(user=self.screen_name, *args, **kargs) def followers_ids(self, *args, **kargs): return self._api.followers_ids(user_id=self.id, *args, **kargs) class DirectMessage(Model): @classmethod def parse(cls, api, json): dm = cls(api) for k, v in json.items(): if k == 'sender' or k == 'recipient': setattr(dm, k, User.parse(api, v)) elif k == 'created_at': setattr(dm, k, parse_datetime(v)) else: setattr(dm, k, v) return dm def destroy(self): return self._api.destroy_direct_message(self.id) class Friendship(Model): @classmethod def parse(cls, api, json): relationship = json['relationship'] # parse source source = cls(api) for k, v in relationship['source'].items(): setattr(source, k, v) # parse target target = cls(api) for k, v in relationship['target'].items(): setattr(target, k, v) return source, target class Category(Model): @classmethod def parse(cls, api, json): category = cls(api) for k, v in json.items(): setattr(category, k, v) return category class SavedSearch(Model): @classmethod def parse(cls, api, json): ss = cls(api) for k, v in json.items(): if k == 'created_at': setattr(ss, k, parse_datetime(v)) else: setattr(ss, k, v) return ss def destroy(self): return self._api.destroy_saved_search(self.id) class SearchResults(ResultSet): @classmethod def parse(cls, api, json): metadata = json['search_metadata'] results = SearchResults(metadata.get('max_id'), metadata.get('since_id')) results.refresh_url = metadata.get('refresh_url') results.completed_in = metadata.get('completed_in') results.query = metadata.get('query') for status in json['statuses']: results.append(Status.parse(api, status)) return results class List(Model): @classmethod def parse(cls, api, json): lst = List(api) for k,v in json.items(): if k == 'user': setattr(lst, k, User.parse(api, v)) elif k == 'created_at': setattr(lst, k, parse_datetime(v)) else: setattr(lst, k, v) return lst @classmethod def parse_list(cls, api, json_list, result_set=None): results = ResultSet() if isinstance(json_list, dict): json_list = json_list['lists'] for obj in json_list: results.append(cls.parse(api, obj)) return results def update(self, **kargs): return self._api.update_list(self.slug, **kargs) def destroy(self): return self._api.destroy_list(self.slug) def timeline(self, **kargs): return self._api.list_timeline(self.user.screen_name, self.slug, **kargs) def add_member(self, id): return self._api.add_list_member(self.slug, id) def remove_member(self, id): return self._api.remove_list_member(self.slug, id) def members(self, **kargs): return self._api.list_members(self.user.screen_name, self.slug, **kargs) def is_member(self, id): return self._api.is_list_member(self.user.screen_name, self.slug, id) def subscribe(self): return self._api.subscribe_list(self.user.screen_name, self.slug) def unsubscribe(self): return self._api.unsubscribe_list(self.user.screen_name, self.slug) def subscribers(self, **kargs): return self._api.list_subscribers(self.user.screen_name, self.slug, **kargs) def is_subscribed(self, id): return self._api.is_subscribed_list(self.user.screen_name, self.slug, id) class Relation(Model): @classmethod def parse(cls, api, json): result = cls(api) for k,v in json.items(): if k == 'value' and json['kind'] in ['Tweet', 'LookedupStatus']: setattr(result, k, Status.parse(api, v)) elif k == 'results': setattr(result, k, Relation.parse_list(api, v)) else: setattr(result, k, v) return result class Relationship(Model): @classmethod def parse(cls, api, json): result = cls(api) for k,v in json.items(): if k == 'connections': setattr(result, 'is_following', 'following' in v) setattr(result, 'is_followed_by', 'followed_by' in v) else: setattr(result, k, v) return result class JSONModel(Model): @classmethod def parse(cls, api, json): return json class IDModel(Model): @classmethod def parse(cls, api, json): if isinstance(json, list): return json else: return json['ids'] class BoundingBox(Model): @classmethod def parse(cls, api, json): result = cls(api) if json is not None: for k, v in json.items(): setattr(result, k, v) return result def origin(self): """ Return longitude, latitude of southwest (bottom, left) corner of bounding box, as a tuple. This assumes that bounding box is always a rectangle, which appears to be the case at present. """ return tuple(self.coordinates[0][0]) def corner(self): """ Return longitude, latitude of northeast (top, right) corner of bounding box, as a tuple. This assumes that bounding box is always a rectangle, which appears to be the case at present. """ return tuple(self.coordinates[0][2]) class Place(Model): @classmethod def parse(cls, api, json): place = cls(api) for k, v in json.items(): if k == 'bounding_box': # bounding_box value may be null (None.) # Example: "United States" (id=96683cc9126741d1) if v is not None: t = BoundingBox.parse(api, v) else: t = v setattr(place, k, t) elif k == 'contained_within': # contained_within is a list of Places. setattr(place, k, Place.parse_list(api, v)) else: setattr(place, k, v) return place @classmethod def parse_list(cls, api, json_list): if isinstance(json_list, list): item_list = json_list else: item_list = json_list['result']['places'] results = ResultSet() for obj in item_list: results.append(cls.parse(api, obj)) return results class ModelFactory(object): """ Used by parsers for creating instances of models. You may subclass this factory to add your own extended models. """ status = Status user = User direct_message = DirectMessage friendship = Friendship saved_search = SavedSearch search_results = SearchResults category = Category list = List relation = Relation relationship = Relationship json = JSONModel ids = IDModel place = Place bounding_box = BoundingBox tweepy-2.1/tweepy/oauth.py000066400000000000000000000551621215741226500157160ustar00rootroot00000000000000""" The MIT License Copyright (c) 2007 Leah Culver Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import cgi import urllib import time import random import urlparse import hmac import binascii VERSION = '1.0' # Hi Blaine! HTTP_METHOD = 'GET' SIGNATURE_METHOD = 'PLAINTEXT' class OAuthError(RuntimeError): """Generic exception class.""" def __init__(self, message='OAuth error occured.'): self.message = message def build_authenticate_header(realm=''): """Optional WWW-Authenticate header (401 error)""" return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} def escape(s): """Escape a URL including any /.""" return urllib.quote(s, safe='~') def _utf8_str(s): """Convert unicode to utf-8.""" if isinstance(s, unicode): return s.encode("utf-8") else: return str(s) def generate_timestamp(): """Get seconds since epoch (UTC).""" return int(time.time()) def generate_nonce(length=8): """Generate pseudorandom number.""" return ''.join([str(random.randint(0, 9)) for i in range(length)]) def generate_verifier(length=8): """Generate pseudorandom number.""" return ''.join([str(random.randint(0, 9)) for i in range(length)]) class OAuthConsumer(object): """Consumer of OAuth authentication. OAuthConsumer is a data type that represents the identity of the Consumer via its shared secret with the Service Provider. """ key = None secret = None def __init__(self, key, secret): self.key = key self.secret = secret class OAuthToken(object): """OAuthToken is a data type that represents an End User via either an access or request token. key -- the token secret -- the token secret """ key = None secret = None callback = None callback_confirmed = None verifier = None def __init__(self, key, secret): self.key = key self.secret = secret def set_callback(self, callback): self.callback = callback self.callback_confirmed = 'true' def set_verifier(self, verifier=None): if verifier is not None: self.verifier = verifier else: self.verifier = generate_verifier() def get_callback_url(self): if self.callback and self.verifier: # Append the oauth_verifier. parts = urlparse.urlparse(self.callback) scheme, netloc, path, params, query, fragment = parts[:6] if query: query = '%s&oauth_verifier=%s' % (query, self.verifier) else: query = 'oauth_verifier=%s' % self.verifier return urlparse.urlunparse((scheme, netloc, path, params, query, fragment)) return self.callback def to_string(self): data = { 'oauth_token': self.key, 'oauth_token_secret': self.secret, } if self.callback_confirmed is not None: data['oauth_callback_confirmed'] = self.callback_confirmed return urllib.urlencode(data) def from_string(s): """ Returns a token from something like: oauth_token_secret=xxx&oauth_token=xxx """ params = cgi.parse_qs(s, keep_blank_values=False) key = params['oauth_token'][0] secret = params['oauth_token_secret'][0] token = OAuthToken(key, secret) try: token.callback_confirmed = params['oauth_callback_confirmed'][0] except KeyError: pass # 1.0, no callback confirmed. return token from_string = staticmethod(from_string) def __str__(self): return self.to_string() class OAuthRequest(object): """OAuthRequest represents the request and can be serialized. OAuth parameters: - oauth_consumer_key - oauth_token - oauth_signature_method - oauth_signature - oauth_timestamp - oauth_nonce - oauth_version - oauth_verifier ... any additional parameters, as defined by the Service Provider. """ parameters = None # OAuth parameters. http_method = HTTP_METHOD http_url = None version = VERSION def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): self.http_method = http_method self.http_url = http_url self.parameters = parameters or {} def set_parameter(self, parameter, value): self.parameters[parameter] = value def get_parameter(self, parameter): try: return self.parameters[parameter] except: raise OAuthError('Parameter not found: %s' % parameter) def _get_timestamp_nonce(self): return self.get_parameter('oauth_timestamp'), self.get_parameter( 'oauth_nonce') def get_nonoauth_parameters(self): """Get any non-OAuth parameters.""" parameters = {} for k, v in self.parameters.iteritems(): # Ignore oauth parameters. if k.find('oauth_') < 0: parameters[k] = v return parameters def to_header(self, realm=''): """Serialize as a header for an HTTPAuth request.""" auth_header = 'OAuth realm="%s"' % realm # Add the oauth parameters. if self.parameters: for k, v in self.parameters.iteritems(): if k[:6] == 'oauth_': auth_header += ', %s="%s"' % (k, escape(str(v))) return {'Authorization': auth_header} def to_postdata(self): """Serialize as post data for a POST request.""" return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \ for k, v in self.parameters.iteritems()]) def to_url(self): """Serialize as a URL for a GET request.""" return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) def get_normalized_parameters(self): """Return a string that contains the parameters that must be signed.""" params = self.parameters try: # Exclude the signature if it exists. del params['oauth_signature'] except: pass # Escape key values before sorting. key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \ for k,v in params.items()] # Sort lexicographically, first after key, then after value. key_values.sort() # Combine key value pairs into a string. return '&'.join(['%s=%s' % (k, v) for k, v in key_values]) def get_normalized_http_method(self): """Uppercases the http method.""" return self.http_method.upper() def get_normalized_http_url(self): """Parses the URL and rebuilds it to be scheme://host/path.""" parts = urlparse.urlparse(self.http_url) scheme, netloc, path = parts[:3] # Exclude default port numbers. if scheme == 'http' and netloc[-3:] == ':80': netloc = netloc[:-3] elif scheme == 'https' and netloc[-4:] == ':443': netloc = netloc[:-4] return '%s://%s%s' % (scheme, netloc, path) def sign_request(self, signature_method, consumer, token): """Set the signature parameter to the result of build_signature.""" # Set the signature method. self.set_parameter('oauth_signature_method', signature_method.get_name()) # Set the signature. self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token)) def build_signature(self, signature_method, consumer, token): """Calls the build signature method within the signature method.""" return signature_method.build_signature(self, consumer, token) def from_request(http_method, http_url, headers=None, parameters=None, query_string=None): """Combines multiple parameter sources.""" if parameters is None: parameters = {} # Headers if headers and 'Authorization' in headers: auth_header = headers['Authorization'] # Check that the authorization header is OAuth. if auth_header[:6] == 'OAuth ': auth_header = auth_header[6:] try: # Get the parameters from the header. header_params = OAuthRequest._split_header(auth_header) parameters.update(header_params) except: raise OAuthError('Unable to parse OAuth parameters from ' 'Authorization header.') # GET or POST query string. if query_string: query_params = OAuthRequest._split_url_string(query_string) parameters.update(query_params) # URL parameters. param_str = urlparse.urlparse(http_url)[4] # query url_params = OAuthRequest._split_url_string(param_str) parameters.update(url_params) if parameters: return OAuthRequest(http_method, http_url, parameters) return None from_request = staticmethod(from_request) def from_consumer_and_token(oauth_consumer, token=None, callback=None, verifier=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} defaults = { 'oauth_consumer_key': oauth_consumer.key, 'oauth_timestamp': generate_timestamp(), 'oauth_nonce': generate_nonce(), 'oauth_version': OAuthRequest.version, } defaults.update(parameters) parameters = defaults if token: parameters['oauth_token'] = token.key if token.callback: parameters['oauth_callback'] = token.callback # 1.0a support for verifier. if verifier: parameters['oauth_verifier'] = verifier elif callback: # 1.0a support for callback in the request token request. parameters['oauth_callback'] = callback return OAuthRequest(http_method, http_url, parameters) from_consumer_and_token = staticmethod(from_consumer_and_token) def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): if not parameters: parameters = {} parameters['oauth_token'] = token.key if callback: parameters['oauth_callback'] = callback return OAuthRequest(http_method, http_url, parameters) from_token_and_callback = staticmethod(from_token_and_callback) def _split_header(header): """Turn Authorization: header into parameters.""" params = {} parts = header.split(',') for param in parts: # Ignore realm parameter. if param.find('realm') > -1: continue # Remove whitespace. param = param.strip() # Split key-value. param_parts = param.split('=', 1) # Remove quotes and unescape the value. params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) return params _split_header = staticmethod(_split_header) def _split_url_string(param_str): """Turn URL string into parameters.""" parameters = cgi.parse_qs(param_str, keep_blank_values=False) for k, v in parameters.iteritems(): parameters[k] = urllib.unquote(v[0]) return parameters _split_url_string = staticmethod(_split_url_string) class OAuthServer(object): """A worker to check the validity of a request against a data store.""" timestamp_threshold = 300 # In seconds, five minutes. version = VERSION signature_methods = None data_store = None def __init__(self, data_store=None, signature_methods=None): self.data_store = data_store self.signature_methods = signature_methods or {} def set_data_store(self, data_store): self.data_store = data_store def get_data_store(self): return self.data_store def add_signature_method(self, signature_method): self.signature_methods[signature_method.get_name()] = signature_method return self.signature_methods def fetch_request_token(self, oauth_request): """Processes a request_token request and returns the request token on success. """ try: # Get the request token for authorization. token = self._get_token(oauth_request, 'request') except OAuthError: # No token required for the initial token request. version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) try: callback = self.get_callback(oauth_request) except OAuthError: callback = None # 1.0, no callback specified. self._check_signature(oauth_request, consumer, None) # Fetch a new token. token = self.data_store.fetch_request_token(consumer, callback) return token def fetch_access_token(self, oauth_request): """Processes an access_token request and returns the access token on success. """ version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) try: verifier = self._get_verifier(oauth_request) except OAuthError: verifier = None # Get the request token. token = self._get_token(oauth_request, 'request') self._check_signature(oauth_request, consumer, token) new_token = self.data_store.fetch_access_token(consumer, token, verifier) return new_token def verify_request(self, oauth_request): """Verifies an api call and checks all the parameters.""" # -> consumer and token version = self._get_version(oauth_request) consumer = self._get_consumer(oauth_request) # Get the access token. token = self._get_token(oauth_request, 'access') self._check_signature(oauth_request, consumer, token) parameters = oauth_request.get_nonoauth_parameters() return consumer, token, parameters def authorize_token(self, token, user): """Authorize a request token.""" return self.data_store.authorize_request_token(token, user) def get_callback(self, oauth_request): """Get the callback URL.""" return oauth_request.get_parameter('oauth_callback') def build_authenticate_header(self, realm=''): """Optional support for the authenticate header.""" return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} def _get_version(self, oauth_request): """Verify the correct version request for this server.""" try: version = oauth_request.get_parameter('oauth_version') except: version = VERSION if version and version != self.version: raise OAuthError('OAuth version %s not supported.' % str(version)) return version def _get_signature_method(self, oauth_request): """Figure out the signature with some defaults.""" try: signature_method = oauth_request.get_parameter( 'oauth_signature_method') except: signature_method = SIGNATURE_METHOD try: # Get the signature method object. signature_method = self.signature_methods[signature_method] except: signature_method_names = ', '.join(self.signature_methods.keys()) raise OAuthError('Signature method %s not supported try one of the ' 'following: %s' % (signature_method, signature_method_names)) return signature_method def _get_consumer(self, oauth_request): consumer_key = oauth_request.get_parameter('oauth_consumer_key') consumer = self.data_store.lookup_consumer(consumer_key) if not consumer: raise OAuthError('Invalid consumer.') return consumer def _get_token(self, oauth_request, token_type='access'): """Try to find the token for the provided request token key.""" token_field = oauth_request.get_parameter('oauth_token') token = self.data_store.lookup_token(token_type, token_field) if not token: raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) return token def _get_verifier(self, oauth_request): return oauth_request.get_parameter('oauth_verifier') def _check_signature(self, oauth_request, consumer, token): timestamp, nonce = oauth_request._get_timestamp_nonce() self._check_timestamp(timestamp) self._check_nonce(consumer, token, nonce) signature_method = self._get_signature_method(oauth_request) try: signature = oauth_request.get_parameter('oauth_signature') except: raise OAuthError('Missing signature.') # Validate the signature. valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature) if not valid_sig: key, base = signature_method.build_signature_base_string( oauth_request, consumer, token) raise OAuthError('Invalid signature. Expected signature base ' 'string: %s' % base) built = signature_method.build_signature(oauth_request, consumer, token) def _check_timestamp(self, timestamp): """Verify that timestamp is recentish.""" timestamp = int(timestamp) now = int(time.time()) lapsed = abs(now - timestamp) if lapsed > self.timestamp_threshold: raise OAuthError('Expired timestamp: given %d and now %s has a ' 'greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold)) def _check_nonce(self, consumer, token, nonce): """Verify that the nonce is uniqueish.""" nonce = self.data_store.lookup_nonce(consumer, token, nonce) if nonce: raise OAuthError('Nonce already used: %s' % str(nonce)) class OAuthClient(object): """OAuthClient is a worker to attempt to execute a request.""" consumer = None token = None def __init__(self, oauth_consumer, oauth_token): self.consumer = oauth_consumer self.token = oauth_token def get_consumer(self): return self.consumer def get_token(self): return self.token def fetch_request_token(self, oauth_request): """-> OAuthToken.""" raise NotImplementedError def fetch_access_token(self, oauth_request): """-> OAuthToken.""" raise NotImplementedError def access_resource(self, oauth_request): """-> Some protected resource.""" raise NotImplementedError class OAuthDataStore(object): """A database abstraction used to lookup consumers and tokens.""" def lookup_consumer(self, key): """-> OAuthConsumer.""" raise NotImplementedError def lookup_token(self, oauth_consumer, token_type, token_token): """-> OAuthToken.""" raise NotImplementedError def lookup_nonce(self, oauth_consumer, oauth_token, nonce): """-> OAuthToken.""" raise NotImplementedError def fetch_request_token(self, oauth_consumer, oauth_callback): """-> OAuthToken.""" raise NotImplementedError def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier): """-> OAuthToken.""" raise NotImplementedError def authorize_request_token(self, oauth_token, user): """-> OAuthToken.""" raise NotImplementedError class OAuthSignatureMethod(object): """A strategy class that implements a signature method.""" def get_name(self): """-> str.""" raise NotImplementedError def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): """-> str key, str raw.""" raise NotImplementedError def build_signature(self, oauth_request, oauth_consumer, oauth_token): """-> str.""" raise NotImplementedError def check_signature(self, oauth_request, consumer, token, signature): built = self.build_signature(oauth_request, consumer, token) return built == signature class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): def get_name(self): return 'HMAC-SHA1' def build_signature_base_string(self, oauth_request, consumer, token): sig = ( escape(oauth_request.get_normalized_http_method()), escape(oauth_request.get_normalized_http_url()), escape(oauth_request.get_normalized_parameters()), ) key = '%s&' % escape(consumer.secret) if token: key += escape(token.secret) raw = '&'.join(sig) return key, raw def build_signature(self, oauth_request, consumer, token): """Builds the base signature string.""" key, raw = self.build_signature_base_string(oauth_request, consumer, token) # HMAC object. try: import hashlib # 2.5 hashed = hmac.new(key, raw, hashlib.sha1) except: import sha # Deprecated hashed = hmac.new(key, raw, sha) # Calculate the digest base 64. return binascii.b2a_base64(hashed.digest())[:-1] class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): def get_name(self): return 'PLAINTEXT' def build_signature_base_string(self, oauth_request, consumer, token): """Concatenates the consumer key and secret.""" sig = '%s&' % escape(consumer.secret) if token: sig = sig + escape(token.secret) return sig, sig def build_signature(self, oauth_request, consumer, token): key, raw = self.build_signature_base_string(oauth_request, consumer, token) return keytweepy-2.1/tweepy/parsers.py000066400000000000000000000050761215741226500162540ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. from tweepy.models import ModelFactory from tweepy.utils import import_simplejson from tweepy.error import TweepError class Parser(object): def parse(self, method, payload): """ Parse the response payload and return the result. Returns a tuple that contains the result data and the cursors (or None if not present). """ raise NotImplementedError def parse_error(self, payload): """ Parse the error message from payload. If unable to parse the message, throw an exception and default error message will be used. """ raise NotImplementedError class RawParser(Parser): def __init__(self): pass def parse(self, method, payload): return payload def parse_error(self, payload): return payload class JSONParser(Parser): payload_format = 'json' def __init__(self): self.json_lib = import_simplejson() def parse(self, method, payload): try: json = self.json_lib.loads(payload) except Exception, e: raise TweepError('Failed to parse JSON payload: %s' % e) needsCursors = method.parameters.has_key('cursor') if needsCursors and isinstance(json, dict) and 'previous_cursor' in json and 'next_cursor' in json: cursors = json['previous_cursor'], json['next_cursor'] return json, cursors else: return json def parse_error(self, payload): error = self.json_lib.loads(payload) if error.has_key('error'): return error['error'] else: return error['errors'] class ModelParser(JSONParser): def __init__(self, model_factory=None): JSONParser.__init__(self) self.model_factory = model_factory or ModelFactory def parse(self, method, payload): try: if method.payload_type is None: return model = getattr(self.model_factory, method.payload_type) except AttributeError: raise TweepError('No model for this payload type: %s' % method.payload_type) json = JSONParser.parse(self, method, payload) if isinstance(json, tuple): json, cursors = json else: cursors = None if method.payload_list: result = model.parse_list(method.api, json) else: result = model.parse(method.api, json) if cursors: return result, cursors else: return result tweepy-2.1/tweepy/streaming.py000066400000000000000000000202531215741226500165600ustar00rootroot00000000000000# Tweepy # Copyright 2009-2010 Joshua Roesslein # See LICENSE for details. import httplib from socket import timeout from threading import Thread from time import sleep from tweepy.models import Status from tweepy.api import API from tweepy.error import TweepError from tweepy.utils import import_simplejson, urlencode_noplus json = import_simplejson() STREAM_VERSION = '1.1' class StreamListener(object): def __init__(self, api=None): self.api = api or API() def on_connect(self): """Called once connected to streaming server. This will be invoked once a successful response is received from the server. Allows the listener to perform some work prior to entering the read loop. """ pass def on_data(self, data): """Called when raw data is received from connection. Override this method if you wish to manually handle the stream data. Return False to stop stream and close connection. """ if 'in_reply_to_status_id' in data: status = Status.parse(self.api, json.loads(data)) if self.on_status(status) is False: return False elif 'delete' in data: delete = json.loads(data)['delete']['status'] if self.on_delete(delete['id'], delete['user_id']) is False: return False elif 'limit' in data: if self.on_limit(json.loads(data)['limit']['track']) is False: return False def on_status(self, status): """Called when a new status arrives""" return def on_delete(self, status_id, user_id): """Called when a delete notice arrives for a status""" return def on_limit(self, track): """Called when a limitation notice arrvies""" return def on_error(self, status_code): """Called when a non-200 status code is returned""" return False def on_timeout(self): """Called when stream connection times out""" return class Stream(object): host = 'stream.twitter.com' def __init__(self, auth, listener, **options): self.auth = auth self.listener = listener self.running = False self.timeout = options.get("timeout", 300.0) self.retry_count = options.get("retry_count") self.retry_time = options.get("retry_time", 10.0) self.snooze_time = options.get("snooze_time", 5.0) self.buffer_size = options.get("buffer_size", 1500) if options.get("secure", True): self.scheme = "https" else: self.scheme = "http" self.api = API() self.headers = options.get("headers") or {} self.parameters = None self.body = None def _run(self): # Authenticate url = "%s://%s%s" % (self.scheme, self.host, self.url) # Connect and process the stream error_counter = 0 conn = None exception = None while self.running: if self.retry_count is not None and error_counter > self.retry_count: # quit if error count greater than retry count break try: if self.scheme == "http": conn = httplib.HTTPConnection(self.host) else: conn = httplib.HTTPSConnection(self.host) self.auth.apply_auth(url, 'POST', self.headers, self.parameters) conn.connect() conn.sock.settimeout(self.timeout) conn.request('POST', self.url, self.body, headers=self.headers) resp = conn.getresponse() if resp.status != 200: if self.listener.on_error(resp.status) is False: break error_counter += 1 sleep(self.retry_time) else: error_counter = 0 self.listener.on_connect() self._read_loop(resp) except timeout: if self.listener.on_timeout() == False: break if self.running is False: break conn.close() sleep(self.snooze_time) except Exception, exception: # any other exception is fatal, so kill loop break # cleanup self.running = False if conn: conn.close() if exception: raise def _data(self, data): if self.listener.on_data(data) is False: self.running = False def _read_loop(self, resp): while self.running and not resp.isclosed(): # Note: keep-alive newlines might be inserted before each length value. # read until we get a digit... c = '\n' while c == '\n' and self.running and not resp.isclosed(): c = resp.read(1) delimited_string = c # read rest of delimiter length.. d = '' while d != '\n' and self.running and not resp.isclosed(): d = resp.read(1) delimited_string += d # read the next twitter status object if delimited_string.strip().isdigit(): next_status_obj = resp.read( int(delimited_string) ) self._data(next_status_obj) if resp.isclosed(): self.on_closed(resp) def _start(self, async): self.running = True if async: Thread(target=self._run).start() else: self._run() def on_closed(self, resp): """ Called when the response has been closed by Twitter """ pass def userstream(self, count=None, async=False, secure=True): self.parameters = {'delimited': 'length'} if self.running: raise TweepError('Stream object already connected!') self.url = '/2/user.json?delimited=length' self.host='userstream.twitter.com' self._start(async) def firehose(self, count=None, async=False): self.parameters = {'delimited': 'length'} if self.running: raise TweepError('Stream object already connected!') self.url = '/%s/statuses/firehose.json?delimited=length' % STREAM_VERSION if count: self.url += '&count=%s' % count self._start(async) def retweet(self, async=False): self.parameters = {'delimited': 'length'} if self.running: raise TweepError('Stream object already connected!') self.url = '/%s/statuses/retweet.json?delimited=length' % STREAM_VERSION self._start(async) def sample(self, count=None, async=False): self.parameters = {'delimited': 'length'} if self.running: raise TweepError('Stream object already connected!') self.url = '/%s/statuses/sample.json?delimited=length' % STREAM_VERSION if count: self.url += '&count=%s' % count self._start(async) def filter(self, follow=None, track=None, async=False, locations=None, count = None, stall_warnings=False, languages=None): self.parameters = {} self.headers['Content-type'] = "application/x-www-form-urlencoded" if self.running: raise TweepError('Stream object already connected!') self.url = '/%s/statuses/filter.json?delimited=length' % STREAM_VERSION if follow: self.parameters['follow'] = ','.join(map(str, follow)) if track: self.parameters['track'] = ','.join(map(str, track)) if locations and len(locations) > 0: assert len(locations) % 4 == 0 self.parameters['locations'] = ','.join(['%.2f' % l for l in locations]) if count: self.parameters['count'] = count if stall_warnings: self.parameters['stall_warnings'] = stall_warnings if languages: self.parameters['language'] = ','.join(map(str, languages)) self.body = urlencode_noplus(self.parameters) self.parameters['delimited'] = 'length' self._start(async) def disconnect(self): if self.running is False: return self.running = False tweepy-2.1/tweepy/utils.py000066400000000000000000000051471215741226500157340ustar00rootroot00000000000000# Tweepy # Copyright 2010 Joshua Roesslein # See LICENSE for details. from datetime import datetime import time import htmlentitydefs import re import locale from urllib import quote def parse_datetime(string): # Set locale for date parsing locale.setlocale(locale.LC_TIME, 'C') # We must parse datetime this way to work in python 2.4 date = datetime(*(time.strptime(string, '%a %b %d %H:%M:%S +0000 %Y')[0:6])) # Reset locale back to the default setting locale.setlocale(locale.LC_TIME, '') return date def parse_html_value(html): return html[html.find('>')+1:html.rfind('<')] def parse_a_href(atag): start = atag.find('"') + 1 end = atag.find('"', start) return atag[start:end] def parse_search_datetime(string): # Set locale for date parsing locale.setlocale(locale.LC_TIME, 'C') # We must parse datetime this way to work in python 2.4 date = datetime(*(time.strptime(string, '%a, %d %b %Y %H:%M:%S +0000')[0:6])) # Reset locale back to the default setting locale.setlocale(locale.LC_TIME, '') return date def unescape_html(text): """Created by Fredrik Lundh (http://effbot.org/zone/re-sub.htm#unescape-html)""" def fixup(m): text = m.group(0) if text[:2] == "&#": # character reference try: if text[:3] == "&#x": return unichr(int(text[3:-1], 16)) else: return unichr(int(text[2:-1])) except ValueError: pass else: # named entity try: text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) except KeyError: pass return text # leave as is return re.sub("&#?\w+;", fixup, text) def convert_to_utf8_str(arg): # written by Michael Norton (http://docondev.blogspot.com/) if isinstance(arg, unicode): arg = arg.encode('utf-8') elif not isinstance(arg, str): arg = str(arg) return arg def import_simplejson(): try: import simplejson as json except ImportError: try: import json # Python 2.6+ except ImportError: try: from django.utils import simplejson as json # Google App Engine except ImportError: raise ImportError, "Can't load a json library" return json def list_to_csv(item_list): if item_list: return ','.join([str(i) for i in item_list]) def urlencode_noplus(query): return '&'.join(['%s=%s' % (quote(str(k)), quote(str(v))) \ for k, v in query.iteritems()])