PyTumblr2


NamePyTumblr2 JSON
Version 0.2.4 PyPI version JSON
download
home_pagehttps://github.com/nostalgebraist/pytumblr
SummaryA Python API v2 wrapper for Tumblr, updated for NPF compliance (and beyond!)
upload_time2023-12-21 20:43:50
maintainer
docs_urlNone
authornostalgebraist
requires_python>=3.6
licenseApache Software License 2.0
keywords pytumblr pytumblr2 tumblr
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            
PyTumblr2
=========
|Build Status|

A fork of `pytumblr <https://github.com/tumblr/pytumblr>`_, updated for the New Post Format era.

Quick demo, if you're familiar with pytumblr:

.. code:: python

    client = pytumblr2.TumblrRestClient(*keys)

    posts = client.posts('nostalgebraist')['posts']
    # by default, post content is fetched in NPF
    posts[0]['blocks']

    # fetch single posts easily
    post = client.get_single_post('nostalgebraist', 642337957436588032)

    client.legacy_conversion_on()
    post = client.get_single_post('nostalgebraist', 642337957436588032)
    # post content was fetched in NPF, then converted to legacy HTML and populated to 'body'
    post['body']

    # returns ratelimit info, from the headers of the most recent API response
    client.get_ratelimit_data()

    # if you're nostalgic for 2015
    client.npf_consumption_off()
    post = client.get_single_post('nostalgebraist', 642337957436588032)
    # post content was fetched in legacy
    post['body']

    # create post in NPF
    response = client.create_post(
        'your_blogname',
        content=[{'type': 'text', 'text': "I'm a bot using the beta editor!"}]
    )

    # reblog the post you just made, in NPF
    # no need for reblog keys / UUIDs
    # the client will fetch them if needed (with caching)
    client.reblog_post(
        'your_blogname',  # reblogging TO
        'your_blogname',  # rebloggin FROM
        response["id"],
        content=[{'type': 'text', 'text': "I'm reblogging myself"}]
    )

    # fetch notifications (the items that appear on the activity page)
    response = client.notifications('your_blogname')

Planned features that aren't implemented yet:
        - helpers for pagination
        - helpers for load balancing across clients

Installation
============

Install via pip:

.. code-block:: bash

    $ pip install pytumblr2

Install from source:

.. code-block:: bash

    $ git clone https://github.com/tumblr/pytumblr2.git
    $ cd pytumblr2
    $ python setup.py install

Usage
=====

Create a client
---------------

A ``pytumblr2.TumblrRestClient`` is the object you'll make all of your calls to the Tumblr API through. Creating one is this easy:

.. code:: python

    client = pytumblr2.TumblrRestClient(
        '<consumer_key>',
        '<consumer_secret>',
        '<oauth_token>',
        '<oauth_secret>',
    )

    client.info() # Grabs the current user information

Two easy ways to get your credentials to are:

1. The built-in ``interactive_console.py`` tool (if you already have a consumer key & secret)
2. The Tumblr API console at https://api.tumblr.com/console
3. Get sample login code at https://api.tumblr.com/console/calls/user/info

Consuming posts in NPF and legacy
---------------------------------

By default, methods that fetch posts will fetch them in NPF.

To control this, use

.. code:: python

    # after client construction
    client.npf_consumption_off()  # use legacy consumption, i.e. npf=false param in the API
    client.npf_consumption_on()  # use NPF consumption, i.e. npf=true param in the API

    # during client construction
    client = pytumblr2.TumblrRestClient(..., consume_in_npf_by_default=False)  # legacy consumption
    client = pytumblr2.TumblrRestClient(..., consume_in_npf_by_default=True)  # NPF consumption

Note that NPF consumption is `strongly recommended by the developers of tumblr <https://github.com/tumblr/docs/blob/master/api.md#response-12>`_.

Using PyTumblr2's native NPF-to-HTML conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you prefer parsing HTML to parsing NPF, PyTumblr2 supports two ways of fetching posts in HTML/legacy format.

First, you can turn NPF consumption off, as described above.  When you fetch a post that was created in NPF, this will use tumblr's internal NPF-to-legacy conversion to produce a legacy response.

Second, you can use PyTumblr2's own NPF-to-legacy converter.  To do this:

.. code:: python

    # after client construction
    client.npf_consumption_on()
    client.legacy_conversion_on()

    # during client construction
    client = pytumblr2.TumblrRestClient(..., consume_in_npf_by_default=True, convert_npf_to_legacy_html=True)

A client in this state will return "hybrid" responses, containing fields from both NPF and legacy payloads:

- The response will contain NPF fields like ``content``. These come directly from the tumblr API response.
- The response will also contain legacy fields like ``body``. These were generated from the API response by PyTumblr2's converter.

Differences between PyTumblr2's converter and tumblr's:

- It behaves better in some cases where tumblr's converter fails, generally involving blockquotes. `Example <https://github.com/tumblr/docs/issues/36>`_
- It is not fully featured, and focused on text and image content. For example, it simply ignores videos.

Supported Methods
-----------------

User Methods
~~~~~~~~~~~~

.. code:: python

    client.info() # get information about the authenticating user
    client.dashboard() # get the dashboard for the authenticating user
    client.likes() # get the likes for the authenticating user
    client.following() # get the blogs followed by the authenticating user

    client.follow('codingjester.tumblr.com') # follow a blog
    client.unfollow('codingjester.tumblr.com') # unfollow a blog

    client.like(id, reblogkey) # like a post
    client.unlike(id, reblogkey) # unlike a post

Blog Methods
~~~~~~~~~~~~

.. code:: python

    client.blog_info(blogName) # get information about a blog
    client.posts(blogName, **params) # get posts for a blog
    client.get_single_post(blogName, id , **params) # get a single post
    client.avatar(blogName) # get the avatar for a blog
    client.blog_likes(blogName) # get the likes on a blog
    client.followers(blogName) # get the followers of a blog
    client.blog_following(blogName) # get the publicly exposed blogs that [blogName] follows
    client.queue(blogName) # get the queue for a given blog
    client.submission(blogName) # get the submissions for a given blog

Post creation and editing
-----------------------------

General note on using these methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Post creation and editing methods take a variety of keyword arguments.  Outside of a few special cases, these arguments are passed on directly to the tumblr API as key-value pairs in the json payload.

For example, the API spec `says <https://github.com/tumblr/docs/blob/master/api.md#request-parameters-24>`_  says ``content`` is a required field when creating an NPF post.  In PyTumblr2, you'll provide the value of this field by passing an argument ``content=[...]`` to the method ``create_post``.

For guidance on constructing these requests, you should consult

- `The tumblr API spec <https://github.com/tumblr/docs/blob/master/api.md>`_
    - for the names and meanings of the JSON fields that the API accepts in each type of request (e.g. "create NPF post," "edit legacy post")

- `The NPF spec <https://github.com/tumblr/docs/blob/master/npf-spec.md>`_
    - for information about how to compose posts in NPF using the ``content`` and (optionally) ``layout`` JSON fields


Creating posts
~~~~~~~~~~~~~~

Create posts in NPF with ``create_post``:

.. code:: python

    client.create_post(blogName, content=[{'type': 'text', 'text': "my post"}])

To create an NPF post containing media, pass an additional argument ``media_sources``.  The value should be a dict mapping each identifiers from the post's media blocks to a file path or file object.

.. code:: python

    client.create_post(
        blogName,
        content=[
            {"type": "text", 'text': "cool picture"},
            {"type": "image", "media": [{"type": "image/jpeg", "identifier": "my_media_identifier"}]}},
        ],
        media_sources={"my_media_identifier": "/Users/johnb/path/to/my/image.jpg"}
    )

If you want to create a legacy post, use one of the methods with a ``legacy_create_`` prefix.  For example:

.. code:: python

    #Creating a text post
    client.legacy_create_text(blogName, state="published", slug="testing-text-posts", title="Testing", body="testing1 2 3 4")

    #Creates a photo post using a source URL
    client.legacy_create_photo(blogName, state="published", tags=["testing", "ok"],
                               source="https://68.media.tumblr.com/b965fbb2e501610a29d80ffb6fb3e1ad/tumblr_n55vdeTse11rn1906o1_500.jpg")

    #Creates a photo post using a local filepath
    client.legacy_create_photo(blogName, state="queue", tags=["testing", "ok"],
                               tweet="Woah this is an incredible sweet post [URL]",
                               data="/Users/johnb/path/to/my/image.jpg")

    #Creates a photoset post using several local filepaths
    client.legacy_create_photo(blogName, state="draft", tags=["jb is cool"], format="markdown",
                               data=["/Users/johnb/path/to/my/image.jpg", "/Users/johnb/Pictures/kittens.jpg"],
                               caption="## Mega sweet kittens")

Editing a post
~~~~~~~~~~~~~~

Edit in NPF:

.. code:: python

    client.edit_post(blogName, post_id, content=[{'type': 'text', 'text': "edited"}])

Edit in legacy:

.. code:: python

    client.legacy_edit_post(blogName, id=post_id, type="photo", data="/Users/johnb/mega/awesome.jpg")

Reblogging a Post
~~~~~~~~~~~~~~~~~

Reblog in NPF, using your blog name, the target blog name, and the target post ID:

.. code:: python

    client.reblog_post(blogName, 'blog_to_reblog_from', 125356)

Reblogging a post requires a reblog key and (in NPF) a blog UUID.  These can only be obtained via a GET request on the post.

Under the hood, the client will send this GET request if it doesn't have the key and UUID.  These values are cached, so this will only happen once per client object and post.

Reblog in legacy:

.. code:: python

    client.legacy_reblog(blogName, id=125356, reblog_key="reblog_key")

Other methods
-----------------

Deleting a post
~~~~~~~~~~~~~~~

Deleting just requires that you own the post and have the post id

.. code:: python

    client.delete_post(blogName, 123456) # Deletes your post :(

A note on tags: When passing tags, as params, please pass them as a list (not a comma-separated string):

.. code:: python

    client.create_text(blogName, tags=['hello', 'world'], ...)

Getting notes for a post
~~~~~~~~~~~~~~~~~~~~~~~~

In order to get the notes for a post, you need to have the post id and the blog that it is on.

.. code:: python

    data = client.notes(blogName, id='123456')

The results include a timestamp you can use to make future calls.

.. code:: python

    data = client.notes(blogName, id='123456', before_timestamp=data["_links"]["next"]["query_params"]["before_timestamp"])

Getting notifications
~~~~~~~~~~~~~~~~~~~~~~~~

Notifications are the items that appear on a user's activity page.  You can fetch them like this:

.. code:: python

    data = client.notifications(blogName)

The results include a timestamp you can use to make future calls.

.. code:: python

    data = client.notifications(blogName, before=data["_links"]["next"]["query_params"]["before"])

Tagged Methods
~~~~~~~~~~~~~~

.. code:: python

    # get posts with a given tag
    client.tagged(tag, **params)

Using the interactive console
-----------------------------

This client comes with a nice interactive console to run you through the OAuth process, grab your tokens (and store them for future use).

You'll need ``pyyaml`` installed to run it, but then it's just:

.. code:: bash

    $ python interactive-console.py

and away you go! Tokens are stored in ``~/.tumblr`` and are also shared by other Tumblr API clients like the Ruby client.

Running tests
-------------

The tests (and coverage reports) are run with nose, like this:

.. code:: bash

    python setup.py test

Copyright and license
=====================

Copyright 2021 nostalgebraist

Copyright 2013 Tumblr, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:

http://www.apache.org/licenses/LICENSE-2.0

The Initial Developer of some parts of the framework, which are copied from, derived from, or
inspired by Pytumblr (via Apache Flex), is Tumblr, Inc. (https://www.tumblr.com/).

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations.

.. |Build Status| image:: https://app.travis-ci.com/nostalgebraist/pytumblr2.png?branch=master
   :target: https://app.travis-ci.com/nostalgebraist/pytumblr2

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/nostalgebraist/pytumblr",
    "name": "PyTumblr2",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": "",
    "keywords": "pytumblr,pytumblr2,tumblr",
    "author": "nostalgebraist",
    "author_email": "nostalgebraist@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/80/9d/0c486c28bc660eff72072179f2afbc96c4f6bdddb43a4eb3b020c4aa54fa/PyTumblr2-0.2.4.tar.gz",
    "platform": null,
    "description": "\nPyTumblr2\n=========\n|Build Status|\n\nA fork of `pytumblr <https://github.com/tumblr/pytumblr>`_, updated for the New Post Format era.\n\nQuick demo, if you're familiar with pytumblr:\n\n.. code:: python\n\n    client = pytumblr2.TumblrRestClient(*keys)\n\n    posts = client.posts('nostalgebraist')['posts']\n    # by default, post content is fetched in NPF\n    posts[0]['blocks']\n\n    # fetch single posts easily\n    post = client.get_single_post('nostalgebraist', 642337957436588032)\n\n    client.legacy_conversion_on()\n    post = client.get_single_post('nostalgebraist', 642337957436588032)\n    # post content was fetched in NPF, then converted to legacy HTML and populated to 'body'\n    post['body']\n\n    # returns ratelimit info, from the headers of the most recent API response\n    client.get_ratelimit_data()\n\n    # if you're nostalgic for 2015\n    client.npf_consumption_off()\n    post = client.get_single_post('nostalgebraist', 642337957436588032)\n    # post content was fetched in legacy\n    post['body']\n\n    # create post in NPF\n    response = client.create_post(\n        'your_blogname',\n        content=[{'type': 'text', 'text': \"I'm a bot using the beta editor!\"}]\n    )\n\n    # reblog the post you just made, in NPF\n    # no need for reblog keys / UUIDs\n    # the client will fetch them if needed (with caching)\n    client.reblog_post(\n        'your_blogname',  # reblogging TO\n        'your_blogname',  # rebloggin FROM\n        response[\"id\"],\n        content=[{'type': 'text', 'text': \"I'm reblogging myself\"}]\n    )\n\n    # fetch notifications (the items that appear on the activity page)\n    response = client.notifications('your_blogname')\n\nPlanned features that aren't implemented yet:\n        - helpers for pagination\n        - helpers for load balancing across clients\n\nInstallation\n============\n\nInstall via pip:\n\n.. code-block:: bash\n\n    $ pip install pytumblr2\n\nInstall from source:\n\n.. code-block:: bash\n\n    $ git clone https://github.com/tumblr/pytumblr2.git\n    $ cd pytumblr2\n    $ python setup.py install\n\nUsage\n=====\n\nCreate a client\n---------------\n\nA ``pytumblr2.TumblrRestClient`` is the object you'll make all of your calls to the Tumblr API through. Creating one is this easy:\n\n.. code:: python\n\n    client = pytumblr2.TumblrRestClient(\n        '<consumer_key>',\n        '<consumer_secret>',\n        '<oauth_token>',\n        '<oauth_secret>',\n    )\n\n    client.info() # Grabs the current user information\n\nTwo easy ways to get your credentials to are:\n\n1. The built-in ``interactive_console.py`` tool (if you already have a consumer key & secret)\n2. The Tumblr API console at https://api.tumblr.com/console\n3. Get sample login code at https://api.tumblr.com/console/calls/user/info\n\nConsuming posts in NPF and legacy\n---------------------------------\n\nBy default, methods that fetch posts will fetch them in NPF.\n\nTo control this, use\n\n.. code:: python\n\n    # after client construction\n    client.npf_consumption_off()  # use legacy consumption, i.e. npf=false param in the API\n    client.npf_consumption_on()  # use NPF consumption, i.e. npf=true param in the API\n\n    # during client construction\n    client = pytumblr2.TumblrRestClient(..., consume_in_npf_by_default=False)  # legacy consumption\n    client = pytumblr2.TumblrRestClient(..., consume_in_npf_by_default=True)  # NPF consumption\n\nNote that NPF consumption is `strongly recommended by the developers of tumblr <https://github.com/tumblr/docs/blob/master/api.md#response-12>`_.\n\nUsing PyTumblr2's native NPF-to-HTML conversation\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you prefer parsing HTML to parsing NPF, PyTumblr2 supports two ways of fetching posts in HTML/legacy format.\n\nFirst, you can turn NPF consumption off, as described above.  When you fetch a post that was created in NPF, this will use tumblr's internal NPF-to-legacy conversion to produce a legacy response.\n\nSecond, you can use PyTumblr2's own NPF-to-legacy converter.  To do this:\n\n.. code:: python\n\n    # after client construction\n    client.npf_consumption_on()\n    client.legacy_conversion_on()\n\n    # during client construction\n    client = pytumblr2.TumblrRestClient(..., consume_in_npf_by_default=True, convert_npf_to_legacy_html=True)\n\nA client in this state will return \"hybrid\" responses, containing fields from both NPF and legacy payloads:\n\n- The response will contain NPF fields like ``content``. These come directly from the tumblr API response.\n- The response will also contain legacy fields like ``body``. These were generated from the API response by PyTumblr2's converter.\n\nDifferences between PyTumblr2's converter and tumblr's:\n\n- It behaves better in some cases where tumblr's converter fails, generally involving blockquotes. `Example <https://github.com/tumblr/docs/issues/36>`_\n- It is not fully featured, and focused on text and image content. For example, it simply ignores videos.\n\nSupported Methods\n-----------------\n\nUser Methods\n~~~~~~~~~~~~\n\n.. code:: python\n\n    client.info() # get information about the authenticating user\n    client.dashboard() # get the dashboard for the authenticating user\n    client.likes() # get the likes for the authenticating user\n    client.following() # get the blogs followed by the authenticating user\n\n    client.follow('codingjester.tumblr.com') # follow a blog\n    client.unfollow('codingjester.tumblr.com') # unfollow a blog\n\n    client.like(id, reblogkey) # like a post\n    client.unlike(id, reblogkey) # unlike a post\n\nBlog Methods\n~~~~~~~~~~~~\n\n.. code:: python\n\n    client.blog_info(blogName) # get information about a blog\n    client.posts(blogName, **params) # get posts for a blog\n    client.get_single_post(blogName, id , **params) # get a single post\n    client.avatar(blogName) # get the avatar for a blog\n    client.blog_likes(blogName) # get the likes on a blog\n    client.followers(blogName) # get the followers of a blog\n    client.blog_following(blogName) # get the publicly exposed blogs that [blogName] follows\n    client.queue(blogName) # get the queue for a given blog\n    client.submission(blogName) # get the submissions for a given blog\n\nPost creation and editing\n-----------------------------\n\nGeneral note on using these methods\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPost creation and editing methods take a variety of keyword arguments.  Outside of a few special cases, these arguments are passed on directly to the tumblr API as key-value pairs in the json payload.\n\nFor example, the API spec `says <https://github.com/tumblr/docs/blob/master/api.md#request-parameters-24>`_  says ``content`` is a required field when creating an NPF post.  In PyTumblr2, you'll provide the value of this field by passing an argument ``content=[...]`` to the method ``create_post``.\n\nFor guidance on constructing these requests, you should consult\n\n- `The tumblr API spec <https://github.com/tumblr/docs/blob/master/api.md>`_\n    - for the names and meanings of the JSON fields that the API accepts in each type of request (e.g. \"create NPF post,\" \"edit legacy post\")\n\n- `The NPF spec <https://github.com/tumblr/docs/blob/master/npf-spec.md>`_\n    - for information about how to compose posts in NPF using the ``content`` and (optionally) ``layout`` JSON fields\n\n\nCreating posts\n~~~~~~~~~~~~~~\n\nCreate posts in NPF with ``create_post``:\n\n.. code:: python\n\n    client.create_post(blogName, content=[{'type': 'text', 'text': \"my post\"}])\n\nTo create an NPF post containing media, pass an additional argument ``media_sources``.  The value should be a dict mapping each identifiers from the post's media blocks to a file path or file object.\n\n.. code:: python\n\n    client.create_post(\n        blogName,\n        content=[\n            {\"type\": \"text\", 'text': \"cool picture\"},\n            {\"type\": \"image\", \"media\": [{\"type\": \"image/jpeg\", \"identifier\": \"my_media_identifier\"}]}},\n        ],\n        media_sources={\"my_media_identifier\": \"/Users/johnb/path/to/my/image.jpg\"}\n    )\n\nIf you want to create a legacy post, use one of the methods with a ``legacy_create_`` prefix.  For example:\n\n.. code:: python\n\n    #Creating a text post\n    client.legacy_create_text(blogName, state=\"published\", slug=\"testing-text-posts\", title=\"Testing\", body=\"testing1 2 3 4\")\n\n    #Creates a photo post using a source URL\n    client.legacy_create_photo(blogName, state=\"published\", tags=[\"testing\", \"ok\"],\n                               source=\"https://68.media.tumblr.com/b965fbb2e501610a29d80ffb6fb3e1ad/tumblr_n55vdeTse11rn1906o1_500.jpg\")\n\n    #Creates a photo post using a local filepath\n    client.legacy_create_photo(blogName, state=\"queue\", tags=[\"testing\", \"ok\"],\n                               tweet=\"Woah this is an incredible sweet post [URL]\",\n                               data=\"/Users/johnb/path/to/my/image.jpg\")\n\n    #Creates a photoset post using several local filepaths\n    client.legacy_create_photo(blogName, state=\"draft\", tags=[\"jb is cool\"], format=\"markdown\",\n                               data=[\"/Users/johnb/path/to/my/image.jpg\", \"/Users/johnb/Pictures/kittens.jpg\"],\n                               caption=\"## Mega sweet kittens\")\n\nEditing a post\n~~~~~~~~~~~~~~\n\nEdit in NPF:\n\n.. code:: python\n\n    client.edit_post(blogName, post_id, content=[{'type': 'text', 'text': \"edited\"}])\n\nEdit in legacy:\n\n.. code:: python\n\n    client.legacy_edit_post(blogName, id=post_id, type=\"photo\", data=\"/Users/johnb/mega/awesome.jpg\")\n\nReblogging a Post\n~~~~~~~~~~~~~~~~~\n\nReblog in NPF, using your blog name, the target blog name, and the target post ID:\n\n.. code:: python\n\n    client.reblog_post(blogName, 'blog_to_reblog_from', 125356)\n\nReblogging a post requires a reblog key and (in NPF) a blog UUID.  These can only be obtained via a GET request on the post.\n\nUnder the hood, the client will send this GET request if it doesn't have the key and UUID.  These values are cached, so this will only happen once per client object and post.\n\nReblog in legacy:\n\n.. code:: python\n\n    client.legacy_reblog(blogName, id=125356, reblog_key=\"reblog_key\")\n\nOther methods\n-----------------\n\nDeleting a post\n~~~~~~~~~~~~~~~\n\nDeleting just requires that you own the post and have the post id\n\n.. code:: python\n\n    client.delete_post(blogName, 123456) # Deletes your post :(\n\nA note on tags: When passing tags, as params, please pass them as a list (not a comma-separated string):\n\n.. code:: python\n\n    client.create_text(blogName, tags=['hello', 'world'], ...)\n\nGetting notes for a post\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn order to get the notes for a post, you need to have the post id and the blog that it is on.\n\n.. code:: python\n\n    data = client.notes(blogName, id='123456')\n\nThe results include a timestamp you can use to make future calls.\n\n.. code:: python\n\n    data = client.notes(blogName, id='123456', before_timestamp=data[\"_links\"][\"next\"][\"query_params\"][\"before_timestamp\"])\n\nGetting notifications\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nNotifications are the items that appear on a user's activity page.  You can fetch them like this:\n\n.. code:: python\n\n    data = client.notifications(blogName)\n\nThe results include a timestamp you can use to make future calls.\n\n.. code:: python\n\n    data = client.notifications(blogName, before=data[\"_links\"][\"next\"][\"query_params\"][\"before\"])\n\nTagged Methods\n~~~~~~~~~~~~~~\n\n.. code:: python\n\n    # get posts with a given tag\n    client.tagged(tag, **params)\n\nUsing the interactive console\n-----------------------------\n\nThis client comes with a nice interactive console to run you through the OAuth process, grab your tokens (and store them for future use).\n\nYou'll need ``pyyaml`` installed to run it, but then it's just:\n\n.. code:: bash\n\n    $ python interactive-console.py\n\nand away you go! Tokens are stored in ``~/.tumblr`` and are also shared by other Tumblr API clients like the Ruby client.\n\nRunning tests\n-------------\n\nThe tests (and coverage reports) are run with nose, like this:\n\n.. code:: bash\n\n    python setup.py test\n\nCopyright and license\n=====================\n\nCopyright 2021 nostalgebraist\n\nCopyright 2013 Tumblr, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nThe Initial Developer of some parts of the framework, which are copied from, derived from, or\ninspired by Pytumblr (via Apache Flex), is Tumblr, Inc. (https://www.tumblr.com/).\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations.\n\n.. |Build Status| image:: https://app.travis-ci.com/nostalgebraist/pytumblr2.png?branch=master\n   :target: https://app.travis-ci.com/nostalgebraist/pytumblr2\n",
    "bugtrack_url": null,
    "license": "Apache Software License 2.0",
    "summary": "A Python API v2 wrapper for Tumblr, updated for NPF compliance (and beyond!)",
    "version": "0.2.4",
    "project_urls": {
        "Homepage": "https://github.com/nostalgebraist/pytumblr"
    },
    "split_keywords": [
        "pytumblr",
        "pytumblr2",
        "tumblr"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "90faa8d7efd9eb077b418b02735a90966d328a481f53eac1bc40f834a867e2ec",
                "md5": "d593e0cecae3c8e820db30471be700a7",
                "sha256": "f26a552c10cf1d143355bf9cda38014d0c35c41227fdf5b4a5a3559d3ecc4106"
            },
            "downloads": -1,
            "filename": "PyTumblr2-0.2.4-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d593e0cecae3c8e820db30471be700a7",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=3.6",
            "size": 24545,
            "upload_time": "2023-12-21T20:43:49",
            "upload_time_iso_8601": "2023-12-21T20:43:49.483157Z",
            "url": "https://files.pythonhosted.org/packages/90/fa/a8d7efd9eb077b418b02735a90966d328a481f53eac1bc40f834a867e2ec/PyTumblr2-0.2.4-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "809d0c486c28bc660eff72072179f2afbc96c4f6bdddb43a4eb3b020c4aa54fa",
                "md5": "b66e41a7ff28fcc7f04e575e903240c8",
                "sha256": "c31928d60f64f74fb5882884aefcda095ffba6bd4538a1a3a325f2ab0971780e"
            },
            "downloads": -1,
            "filename": "PyTumblr2-0.2.4.tar.gz",
            "has_sig": false,
            "md5_digest": "b66e41a7ff28fcc7f04e575e903240c8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 24957,
            "upload_time": "2023-12-21T20:43:50",
            "upload_time_iso_8601": "2023-12-21T20:43:50.999762Z",
            "url": "https://files.pythonhosted.org/packages/80/9d/0c486c28bc660eff72072179f2afbc96c4f6bdddb43a4eb3b020c4aa54fa/PyTumblr2-0.2.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-21 20:43:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "nostalgebraist",
    "github_project": "pytumblr",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": false,
    "tox": true,
    "lcname": "pytumblr2"
}
        
Elapsed time: 0.25139s