{ "info": { "author": "Mitja Pagon", "author_email": "mitja@inueni.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Communications :: Chat", "Topic :: Internet", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "birdy\n=====\n\n``birdy`` is a super awesome Twitter API client for Python in just a\nlittle under 400 LOC.\n\nTL;DR\n-----\n\nFeatures\n~~~~~~~~\n\n- `Future proof dynamic API with full REST and Streaming API\n coverage <#ok-im-sold-but-how-do-i-use-it-how-does-this-dynamic-api-construction-work>`__\n- `OAuth1 (user) and OAuth2 (app) authentication\n workflows <#great-what-about-authorization-how-do-i-get-my-access-tokens>`__\n- `Automatic JSON decoding <#automatic-json-decoding>`__,\n `JSONObject <#jsonobject>`__\n- `ApiResponse <#apiresponse>`__, `StreamResponse <#streamresponse>`__\n objects\n- `Informative exceptions <#informative-exceptions>`__\n- `Easily customizable through\n subclassing <#customize-and-extend-through-subclassing>`__\n- `Built on top of the excellent requests and requests-ouathlib\n libraries <#credits>`__\n\nInstallation\n~~~~~~~~~~~~\n\nThe easiest and recommended way to install ``birdy`` is from\n`PyPI `__\n\n::\n\n pip install birdy\n\nUsage\n~~~~~\n\nImport client and initialize it:\n\n.. code:: python\n\n from birdy.twitter import UserClient\n client = UserClient(CONSUMER_KEY,\n CONSUMER_SECRET,\n ACCESS_TOKEN,\n ACCESS_TOKEN_SECRET)\n\nGET example (**GET users/show**):\n\n.. code:: python\n\n response = client.api.users.show.get(screen_name='twitter')\n response.data\n\nPOST example (**POST statuses/update**):\n\n.. code:: pyhton\n\n response = client.api.statuses.update.post(status='Hello @pybirdy!')\n\nDynamic URL example (**POST statuses/destroy/:id**):\n\n.. code:: python\n\n response = client.api.statuses.destroy['240854986559455234'].post()\n\nStreaming API example (**Public Stream POST statuses/filter**):\n\n.. code:: python\n\n response = client.stream.statuses.filter.post(track='twitter')\n\n for data in response.stream():\n print data\n\nSupported Python version\n------------------------\n\n``birdy`` works with both ``python2`` (2.7+) and ``python3`` (3.4+).\n\nWhy another Python Twitter API client? Aren\u2019t there enough?\n-----------------------------------------------------------\n\nThe concept behind ``birdy`` is so simple and awesome that it just had\nto be done, and the result is a super light weight and easy to use API\nclient, that covers the whole Twitter REST API in just a little under\n400 lines of code.\n\nTo achieve this, ``birdy`` relies on established, battle tested python\nlibraries like ``requests`` and ``requests-ouathlib`` to do the heavy\nlifting, but more importantly it relies on Python\u2019s dynamic nature to\nautomatically construct API calls (no individual wrapper functions for\nAPI resources needed). This allows ``birdy`` to cover all existing\nTwitter API resources and any future additions, without the need to\nupdate ``birdy`` itself.\n\nIncludes full support for both **OAuth1** (user) and **OAuth2**\n(application) authentication workflows.\n\nFinally, ``birdy`` is simple and explicit by design, besides error\nhandling and JSON decoding it doesn\u2019t process the returned data in any\nway, that is left for you to handle (who\u2019d know better what to do with\nit).\n\nOK, I\u2019m sold, but how do I use it? How does this dynamic API construction work?\n-------------------------------------------------------------------------------\n\nThe easiest way to show you is by example. Lets say you want to query\nTwitter for @twitter user information. The Twitter API resource for this\nis **GET users/show** (`Twitter\ndocs `__).\n\nFirst you will need to import a client, here we import UserClient\n(OAuth1) and than initialize it.\n\n.. code:: python\n\n from birdy.twitter import UserClient\n client = UserClient(CONSUMER_KEY,\n CONSUMER_SECRET,\n ACCESS_TOKEN,\n ACCESS_TOKEN_SECRET)\n\nTo query the **GET /users/show** API resource and pass in the parameter\nscreen_name=\u2018twitter\u2019 you do this.\n\n.. code:: python\n\n resource = client.api.users.show\n response = resource.get(screen_name='twitter')\n\nWhat happens here is very simple, ``birdy`` translates the\n``users.show`` part after ``client.api`` into the appropriate API\nresource path (**\u2018users/show\u2019**). Then when you call get() on the\nresource, ``birdy`` constructs a full resource URL, appends any\nparameters passed to get() to it and makes a GET request to that URL and\nreturns the result.\n\nUsually the above example would be shortened to just one line like this.\n\n.. code:: python\n\n response = client.api.users.show.get(screen_name='twitter')\n\nMaking a post request is similar, if for example, you would like to post\na status update, this is how to do it. The API resource is **POST\nstatuses/update** (`Twitter\ndocs `__).\n\n.. code:: python\n\n response = client.api.statuses.update.post(status='Hello @pybirdy!')\n\nLike before the part after ``client.api`` gets converted to the correct\npath, only this time post() is called instead of get(), so ``birdy``\nmakes a POST request and pass parameters (and files) as part of the\nrequest body.\n\nFor cases when dynamic values are part of the API resource URL, like\nwhen deleting a tweet at **POST statuses/destroy/:id** (`Twitter\ndocs `__),\n``birdy`` supports an alternative, dictionary lookup like, syntax. For\nexample, deleting a tweet with id \u2018240854986559455234\u2019 looks like this.\n\n.. code:: python\n\n response = client.api.statuses.destroy['240854986559455234'].post()\n\nBy now it should be clear what happens above, ``birdy`` builds the API\nresource path and than makes a POST request, the only difference is that\npart of the API path is provided like a dictionary key lookup.\n\nActually any call can be written in this alternative syntax, use\nwhichever you prefer. Both syntax forms can be freely combined as in the\nexample above. Some more examples:\n\n.. code:: python\n\n response = client.api['users/show'].get(screen_name='twitter')\n\n response = client.api['users']['show'].get(screen_name='twitter')\n\n response = client.api['statuses/destroy']['240854986559455234'].post()\n\nIs Streaming API supported as well?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSure, since version 0.2, ``birdy`` comes with full support for Streaming\nAPI out of the box. Access to the Streaming API is provided by a special\n``StreamClient``.\n\n ``StreamClient`` can\u2019t be used to obtain access tokens, but you can\n use ``UserClient`` to get them.\n\nTo work with the Streaming API, first import the client and initialize\nit.\n\n.. code:: python\n\n from birdy.twitter import StreamClient\n client = StreamClient(CONSUMER_KEY,\n CONSUMER_SECRET,\n ACCESS_TOKEN,\n ACCESS_TOKEN_SECRET)\n\nTo access resources on the **Public** stream, like **POST\nstatuses/filter** (`Twitter\ndocs `__)\n\n.. code:: python\n\n resource = client.stream.statuses.filter.post(track='twitter')\n\nFor **User** stream resource **GET user** (`Twitter\ndocs `__)\n\n.. code:: python\n\n resource = client.userstream.user.get()\n\nAnd for **Site** stream resource **GET site** (`Twitter\ndocs `__)\n\n.. code:: python\n\n resource = client.sitestream.site.get()\n\nTo access the data in the stream you iterate over ``resource.stream()``\nlike this\n\n.. code:: python\n\n for data in resource.stream():\n print data\n\nGreat, what about authorization? How do I get my access tokens?\n---------------------------------------------------------------\n\n``birdy`` supports both **OAuth1** and **OAuth2** authentication\nworkflows by providing two different clients, a ``UserClient`` and\n``AppClient`` respectively. While requests to API resources, like in\nabove examples are the same in both clients, the workflow for obtaining\naccess tokens is slightly different.\n\n Before you get started, you will need to\n `register `__ your application with\n Twitter, to obtain your application\u2019s ``CONSUMER_KEY`` and\n ``CONSUMER_SECRET``.\n\nOAuth1 workflow for user authenticated requests (UserClient)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nStep 1: Creating a client instance\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFirst you need to import the ``UserClient`` and create an instance with\nyour apps ``CONSUMER_KEY`` and ``CONSUMER_SECRET``.\n\n.. code:: python\n\n from birdy.twitter import UserClient\n\n CONSUMER_KEY = 'YOUR_APPS_CONSUMER_KEY'\n CONSUMER_SECRET = 'YOUR_APPS_CONSUMER_SECRET'\n CALLBACK_URL = 'https://127.0.0.1:8000/callback'\n\n client = UserClient(CONSUMER_KEY, CONSUMER_SECRET)\n\nStep 2: Get request token and authorization URL\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n Pass ``callback_url`` only if you have a Web app, Desktop and Mobile\n apps **do not** require it.\n\nNext you need to fetch request token from Twitter. If you are building a\n*Sign-in with Twitter* type application it\u2019s done like this.\n\n.. code:: python\n\n token = client.get_signin_token(CALLBACK_URL)\n\nOtherwise like this.\n\n.. code:: python\n\n token = client.get_authorize_token(CALLBACK_URL)\n\nSave ``token.oauth_token`` and ``token.oauth_token_secret`` for later\nuser, as this are not the final token and secret.\n\n.. code:: python\n\n ACCESS_TOKEN = token.oauth_token\n ACCESS_TOKEN_SECRET = token.oauth_token_secret\n\nDirect the user to Twitter authorization url obtained from\n``token.auth_url``.\n\nStep 3: OAuth verification\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n If you have a Desktop or Mobile app, ``OAUTH_VERIFIER`` is the PIN\n code, you can skip the part about extraction.\n\nAfter authorizing your application on Twitter, the user will be\nredirected back to the ``callback_url`` provided during client\ninitialization in *Step 1*.\n\nYou will need to extract the ``OAUTH_VERIFIER`` from the URL. Most web\nframeworks provide an easy way of doing this or you can parse the URL\nyourself using ``urlparse`` module (if that is your thing).\n\nDjango and Flask examples:\n\n.. code:: python\n\n #Django\n OAUTH_VERIFIER = request.GET['oauth_verifier']\n\n #Flash\n OAUTH_VERIFIER = request.args.get('oauth_verifier')\n\nOnce you have the ``OAUTH_VERIFIER`` you can use it to obtain the final\naccess token and secret. To do that you will need to create a new\ninstance of ``UserClient``, this time also passing in ``ACCESS_TOKEN``\nand ``ACCESS_TOKEN_SECRET`` obtained in *Step 2* and then fetch the\ntokens.\n\n.. code:: python\n\n client = UserClient(CONSUMER_KEY, CONSUMER_SECRET,\n ACCESS_TOKEN, ACCESS_TOKEN_SECRET)\n\n token = client.get_access_token(OAUTH_VERIFIER)\n\nNow that you have the final access token and secret you can save\n``token.oauth_token`` and ``token.oauth_token_secret`` to the database\nfor later use, also you can use the client to start making API request\nimmediately. For example, you can retrieve the users home timeline like\nthis.\n\n.. code:: python\n\n response = client.api.statuses.home_timeline.get()\n response.data\n\nThat\u2019s it you have successfully authorized the user, retrieved the\ntokens and can now make API calls on their behalf.\n\nOAuth2 workflow for app authenticated requests (AppClient)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. step-1-creating-a-client-instance-1:\n\nStep 1: Creating a client instance\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFor OAuth2 you will be using the ``AppClient``, so first you need to\nimport it and create an instance with your apps ``CONSUMER_KEY`` and\n``CONSUMER_SECRET``.\n\n.. code:: python\n\n from birdy.twitter import AppClient\n\n CONSUMER_KEY = 'YOUR_APPS_CONSUMER_KEY'\n CONSUMER_SECRET = 'YOUR_APPS_CONSUMER_SECRET'\n\n client = AppClient(CONSUMER_KEY, CONSUMER_SECRET)\n\nStep 2: Getting the access token\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nOAuth2 workflow is much simpler compared to OAuth1, to obtain the access\ntoken you simply do this.\n\n.. code:: python\n\n access_token = client.get_access_token()\n\nThat\u2019s it, you can start using the client immediately to make API\nrequest on behalf of the app. It\u2019s recommended you save the\n``access_token`` for later use. You initialize the client with a saved\ntoken like this.\n\n.. code:: python\n\n client = AppClient(CONSUMER_KEY, CONSUMER_SECRET, SAVED_ACCESS_TOKEN)\n\nKeep in mind that OAuth2 authenticated requests are **read-only** and\nnot all API resources are available. Check `Twitter\ndocs `__ for more information.\n\nAny other useful features I should know about?\n----------------------------------------------\n\nOf course, ``birdy`` comes with some handy features, to ease your\ndevelopment, right out of the box. Lets take a look at some of the\ngoodies.\n\nAutomatic JSON decoding\n~~~~~~~~~~~~~~~~~~~~~~~\n\nJSON data returned by the REST and Streaming API is automatically\ndecoded to native Python objects, no extra coding necessary, start using\nthe data right away.\n\nJSONObject\n~~~~~~~~~~\n\nWhen decoding JSON data, ``objects`` are, instead of a regular Python\ndictionary, converted to a ``JSONObject``, which is dictionary subclass\nwith attribute style access in addition to regular dictionary lookup\nstyle, for convenience. The following code produces the same result\n\n.. code:: python\n\n followers_count = response.data['followers_count']\n\n followers_count = response.data.followers_count\n\nApiResponse\n~~~~~~~~~~~\n\nCalls to REST API resources return a ``ApiResponse``, which in addition\nto returned data, also gives you access to response headers (useful for\nchecking rate limits) and resource URL.\n\n.. code:: python\n\n response.data # decoded JSON data\n response.resource_url # resource URL\n response.headers # dictionary containing response HTTP headers\n\nStreamResponse\n~~~~~~~~~~~~~~\n\n``StreamResponse`` is returned when calling Streaming API resources and\nprovides the **stream()** method which returns an iterator used to\nreceive JSON decoded streaming data. Like ``ApiResponse`` it also gives\nyou access to response headers and resource URL.\n\n.. code:: python\n\n response.stream() # a generator method used to iterate over the stream\n\n for data in response.stream():\n print data \n\nInformative exceptions\n~~~~~~~~~~~~~~~~~~~~~~\n\nThere are 4 types of exceptions in ``birdy`` all subclasses of base\n``BirdyException`` (which is never directly raised).\n\n- ``TwitterClientError`` raised for connection and access token\n retrieval errors\n- ``TwitterApiError`` raised when Twitter returns an error\n- ``TwitterAuthError`` raised when authentication fails,\n ``TwitterApiError`` subclass\n- ``TwitterRateLimitError`` raised when rate limit for resource is\n reached, ``TwitterApiError`` subclass\n\n``TwitterApiError`` and ``TwitterClientError`` instances (exepct for\naccess token retrieval errors) provide a informative error description\nwhich includes the resource URL and request method used (very handy when\ntracking errors in logs), also available is the following:\n\n.. code:: python\n\n exception.request_method # HTTP method used to make the request (GET or POST)\n exception.resource_url # URL of the API resource called\n exception.status_code # HTTP status code returned by Twitter\n exception.error_code # error code returned by Twitter\n exception.headers # dictionary containing response HTTP headers\n\nCustomize and extend through subclassing\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n``birdy`` was built with subclassing in mind, if you wish to change the\nway it works, all you have to do is subclass one of the clients and\noverride some methods and you are good to go.\n\n Subclassing a client and then using the subclass instance in your\n codeis actually **the recommended way** of using ``birdy``.\n\nFor example, if you don\u2019t wish to use ``JSONObject`` you have to\noverride **get_json_object_hook()** method.\n\n.. code:: python\n\n from birdy.twitter import UserClient\n\n class MyClient(UserClient):\n @staticmethod\n def get_json_object_hook(data):\n return data\n\n client = MyClient(...)\n response = client.api.users.show.get(screen_name='twitter')\n\nOr maybe, if you want global error handling for common errors, just\noverride **handle_response()** method.\n\n.. code:: python\n\n class MyClient(UserClient):\n def handle_response(self, method, response):\n try:\n response = super(MyClient, self).handle_response(method, response)\n except TwitterApiError, e:\n ...\n # Your error handling code\n ...\n return response\n\nAnother use of subclassing is configuration of ``requests.Session``\ninstance\n(`docs `__)\nused to make HTTP requests, to configure it, you override the\n**configure_oauth_session()** method.\n\n.. code:: python\n\n class MyClient(UserClient):\n def configure_oauth_session(self, session):\n session = super(MyClient, self).configure_oauth_session(session)\n session.proxies = {'http': 'foo.bar:3128'}\n return session\n\nDo you accept contributions and feature requests?\n-------------------------------------------------\n\n**Yes**, both contributions (including feedback) and feature requests\nare welcome, the proper way in both cases is to first open an issue on\n`GitHub `__ and we will take if\nfrom there.\n\n Keep in mind that I work on this project on my free time, so I might\n not be able to respond right way.\n\nCredits\n-------\n\n``birdy`` would not exists if not for the excellent\n`requests `__ and\n`requests-oauthlib `__\nlibraries and the wonderful `Python `__\nprograming language.\n\nQuestion, comments, \u2026\n---------------------\n\nIf you need to contact me, you can find me on Twitter\n([@sect2k](https://twitter.com/sect2k/)).\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/inueni/birdy/", "keywords": "twitter api tweet birdy search", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "birdy", "package_url": "https://pypi.org/project/birdy/", "platform": "", "project_url": "https://pypi.org/project/birdy/", "project_urls": { "Homepage": "https://github.com/inueni/birdy/" }, "release_url": "https://pypi.org/project/birdy/0.3.2/", "requires_dist": null, "requires_python": "", "summary": "birdy is a super awesome Twitter API client for Python.", "version": "0.3.2" }, "last_serial": 3345709, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "6008b5b23612cc8379690f26f51e92f3", "sha256": "4872837f7b44018668ab1fbeb4622ddd0da8b2d20f5c5c31fe76c89c6d6bdc78" }, "downloads": -1, "filename": "birdy-0.1.tar.gz", "has_sig": false, "md5_digest": "6008b5b23612cc8379690f26f51e92f3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8182, "upload_time": "2013-10-14T19:39:18", "url": "https://files.pythonhosted.org/packages/9c/e4/7e2ea876dcaf35ebfa36618cd42f870bf7bdb431f1e8942fd86966557582/birdy-0.1.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "80a6c368727a1a48f899b7a99c7c8821", "sha256": "2deeff04f83c6448bc120918a0ec3ba9d996397c4a86ad4408de07b14f6104a1" }, "downloads": -1, "filename": "birdy-0.1.1.tar.gz", "has_sig": false, "md5_digest": "80a6c368727a1a48f899b7a99c7c8821", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8179, "upload_time": "2013-12-22T11:02:09", "url": "https://files.pythonhosted.org/packages/00/17/9caa154774282ec1d0b905d7e31dc393ddb83c2e5cdc8d1aa3ee9a05647a/birdy-0.1.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "ee4cdffbed8670bd11ee70f8d8ae36d3", "sha256": "396c65a4401673e97accd41d5adfb9eec7e3d093c693f03ce9fa46300b513be7" }, "downloads": -1, "filename": "birdy-0.2.tar.gz", "has_sig": false, "md5_digest": "ee4cdffbed8670bd11ee70f8d8ae36d3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10529, "upload_time": "2014-01-07T18:23:45", "url": "https://files.pythonhosted.org/packages/26/58/5b7329b8685b984558a23c4289e296446aaccdc76b996dcc474541cc385f/birdy-0.2.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "600161500503eeb340b4a7eb528de0bb", "sha256": "0819d92f1fd9b673bdc49e33711095f6d01aaac5ec9b06f4244a89dc497dd0a4" }, "downloads": -1, "filename": "birdy-0.3.1.tar.gz", "has_sig": false, "md5_digest": "600161500503eeb340b4a7eb528de0bb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13269, "upload_time": "2017-11-14T01:33:30", "url": "https://files.pythonhosted.org/packages/05/a4/ff1041fae197c396f7e4506109532cfb56b049b56336be20ca86482b4965/birdy-0.3.1.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "8634e3153b06f173695901fc5249480b", "sha256": "2ef73fb80714e10d67d46676a84416dd05c60e10dad407f051822fb83a6895e2" }, "downloads": -1, "filename": "birdy-0.3.2.tar.gz", "has_sig": false, "md5_digest": "8634e3153b06f173695901fc5249480b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13357, "upload_time": "2017-11-19T12:20:04", "url": "https://files.pythonhosted.org/packages/cc/30/3f825b8d4248ebd9de9d218ba4b931c93be664e077c328c4b6dd19eb9d8a/birdy-0.3.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8634e3153b06f173695901fc5249480b", "sha256": "2ef73fb80714e10d67d46676a84416dd05c60e10dad407f051822fb83a6895e2" }, "downloads": -1, "filename": "birdy-0.3.2.tar.gz", "has_sig": false, "md5_digest": "8634e3153b06f173695901fc5249480b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13357, "upload_time": "2017-11-19T12:20:04", "url": "https://files.pythonhosted.org/packages/cc/30/3f825b8d4248ebd9de9d218ba4b931c93be664e077c328c4b6dd19eb9d8a/birdy-0.3.2.tar.gz" } ] }