`_
-
-Want to help?
--------------
-
-Twython is useful, but ultimately only as useful as the people using it (say that ten times fast!). If you'd like to help, write example code, contribute patches, document things on the wiki, tweet about it. Your help is always appreciated!
diff --git a/docs/_themes/basicstrap/customsidebar.html b/docs/_themes/basicstrap/customsidebar.html
index ac8c583..dbeab99 100644
--- a/docs/_themes/basicstrap/customsidebar.html
+++ b/docs/_themes/basicstrap/customsidebar.html
@@ -1,18 +1,5 @@
-{{ _('Donate') }}
-
-
-Find Twython useful? Consider supporting the author on Gittip:
-
-
-
-
-
-
{{ _('Links') }}
\ No newline at end of file
+
diff --git a/docs/conf.py b/docs/conf.py
index 8f3c3d2..3ffa5b5 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -50,9 +50,9 @@ copyright = u'2013, Ryan McGrath'
# built documents.
#
# The short X.Y version.
-version = '3.7.0'
+version = '3.8.0'
# The full version, including alpha/beta/rc tags.
-release = '3.7.0'
+release = '3.8.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/usage/advanced_usage.rst b/docs/usage/advanced_usage.rst
index bdb0b23..df3bc48 100644
--- a/docs/usage/advanced_usage.rst
+++ b/docs/usage/advanced_usage.rst
@@ -59,15 +59,10 @@ with a status update.
.. code-block:: python
- # Assume you are working with a JPEG
+ # Assuming that you are working with a JPEG
from PIL import Image
- try:
- # Python 3
- from io import StringIO
- except ImportError:
- # Python 2
- from StringIO import StringIO
+ from io import BytesIO
photo = Image.open('/path/to/file/image.jpg')
@@ -76,14 +71,13 @@ with a status update.
height = int((float(photo.size[1]) * float(wpercent)))
photo = photo.resize((basewidth, height), Image.ANTIALIAS)
- image_io = StringIO.StringIO()
+ image_io = BytesIO()
photo.save(image_io, format='JPEG')
# If you do not seek(0), the image will be at the end of the file and
# unable to be read
image_io.seek(0)
-
response = twitter.upload_media(media=image_io)
twitter.update_status(status='Checkout this cool image!', media_ids=[response['media_id']])
diff --git a/examples/get_direct_messages.py b/examples/get_direct_messages.py
index 5a27a06..fa80865 100644
--- a/examples/get_direct_messages.py
+++ b/examples/get_direct_messages.py
@@ -1,17 +1,5 @@
from twython import Twython, TwythonError
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
-
get_list = twitter.get_direct_messages()
-#Returns All Twitter DM information which is a lot in a list format
-
-dm_dict = get_list[0]
-#Sets get_list to a dictionary, the number in the list is the direct message retrieved
-#That means that 0 is the most recent and n-1 is the last DM revieved.
-#You can cycle through all the numbers and it will return the text and the sender id of each
-
-print dm_dict['text']
-#Gets the text from the dictionary
-
-print dm_dict['sender']['id']
-#Gets the ID of the sender
+print(get_list)
diff --git a/examples/get_user_timeline.py b/examples/get_user_timeline.py
index 0a1f4df..55ab427 100644
--- a/examples/get_user_timeline.py
+++ b/examples/get_user_timeline.py
@@ -7,4 +7,4 @@ try:
except TwythonError as e:
print e
-print user_timeline
+print(user_timeline)
diff --git a/setup.py b/setup.py
index 11cc2b4..a3600de 100755
--- a/setup.py
+++ b/setup.py
@@ -8,8 +8,8 @@ try:
except ImportError:
from distutils.core import setup
-__author__ = 'Ryan McGrath '
-__version__ = '3.7.0'
+__author__ = 'Ryan McGrath '
+__version__ = '3.9.1'
packages = [
'twython',
@@ -24,15 +24,15 @@ setup(
name='twython',
version=__version__,
install_requires=['requests>=2.1.0', 'requests_oauthlib>=0.4.0'],
+ python_requires='>=3.5',
author='Ryan McGrath',
- author_email='ryan@venodesigns.net',
- license=open('LICENSE').read(),
+ author_email='ryan@rymc.io',
+ license='MIT',
url='https://github.com/ryanmcgrath/twython/tree/master',
keywords='twitter search api tweet twython stream',
- description='Actively maintained, pure Python wrapper for the \
- Twitter API. Supports both normal and streaming Twitter APIs',
- long_description=open('README.rst').read() + '\n\n' +
- open('HISTORY.rst').read(),
+ description='Actively maintained, pure Python wrapper for the Twitter API. Supports both normal and streaming Twitter APIs',
+ long_description=open('README.md', encoding='utf-8').read() + '\n\n' +open('HISTORY.md', encoding='utf-8').read(),
+ long_description_content_type='text/markdown',
include_package_data=True,
packages=packages,
classifiers=[
@@ -42,10 +42,10 @@ setup(
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Communications :: Chat',
'Topic :: Internet',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
]
)
diff --git a/tests/config.py b/tests/config.py
index 8812b81..10ab3ea 100644
--- a/tests/config.py
+++ b/tests/config.py
@@ -2,10 +2,7 @@
import os
import sys
-if sys.version_info[0] == 2 and sys.version_info[1] == 6:
- import unittest2 as unittest
-else:
- import unittest
+import unittest
app_key = os.environ.get('APP_KEY')
app_secret = os.environ.get('APP_SECRET')
diff --git a/twython/__init__.py b/twython/__init__.py
index dc161d1..7d25ef3 100644
--- a/twython/__init__.py
+++ b/twython/__init__.py
@@ -19,7 +19,7 @@ Questions, comments? ryan@venodesigns.net
"""
__author__ = 'Ryan McGrath '
-__version__ = '3.7.0'
+__version__ = '3.9.1'
from .api import Twython
from .streaming import TwythonStreamer
diff --git a/twython/advisory.py b/twython/advisory.py
index 31657ee..9a0863a 100644
--- a/twython/advisory.py
+++ b/twython/advisory.py
@@ -17,6 +17,6 @@ only TwythonDeprecationWarnings.
class TwythonDeprecationWarning(DeprecationWarning):
"""Custom DeprecationWarning to be raised when methods/variables
are being deprecated in Twython. Python 2.7 > ignores DeprecationWarning
- so we want to specifcally bubble up ONLY Twython Deprecation Warnings
+ so we want to specifically bubble up ONLY Twython Deprecation Warnings
"""
pass
diff --git a/twython/api.py b/twython/api.py
index 4953129..1e786ad 100644
--- a/twython/api.py
+++ b/twython/api.py
@@ -9,6 +9,7 @@ Twitter Authentication, and miscellaneous methods that are useful when
dealing with the Twitter API
"""
+from __future__ import generator_stop
import warnings
import re
@@ -135,13 +136,13 @@ class Twython(EndpointsMixin, object):
def __repr__(self):
return '' % (self.app_key)
- def _request(self, url, method='GET', params=None, api_call=None):
+ def _request(self, url, method='GET', params=None, api_call=None, json_encoded=False):
"""Internal request method"""
method = method.lower()
params = params or {}
func = getattr(self.client, method)
- if isinstance(params, dict):
+ if isinstance(params, dict) and json_encoded is False:
params, files = _transparent_params(params)
else:
params = params
@@ -153,11 +154,16 @@ class Twython(EndpointsMixin, object):
if k in ('timeout', 'allow_redirects', 'stream', 'verify'):
requests_args[k] = v
- if method == 'get':
+ if method == 'get' or method == 'delete':
requests_args['params'] = params
else:
+ # Check for json_encoded so we will sent params as "data" or "json"
+ if json_encoded:
+ data_key = 'json'
+ else:
+ data_key = 'data'
requests_args.update({
- 'data': params,
+ data_key: params,
'files': files,
})
try:
@@ -230,14 +236,14 @@ class Twython(EndpointsMixin, object):
return error_message
- def request(self, endpoint, method='GET', params=None, version='1.1'):
+ def request(self, endpoint, method='GET', params=None, version='1.1', json_encoded=False):
"""Return dict of response received from Twitter's API
:param endpoint: (required) Full url or Twitter API endpoint
(e.g. search/tweets)
:type endpoint: string
:param method: (optional) Method of accessing data, either
- GET or POST. (default GET)
+ GET, POST or DELETE. (default GET)
:type method: string
:param params: (optional) Dict of parameters (if any) accepted
the by Twitter API endpoint you are trying to
@@ -246,6 +252,9 @@ class Twython(EndpointsMixin, object):
:param version: (optional) Twitter API version to access
(default 1.1)
:type version: string
+ :param json_encoded: (optional) Flag to indicate if this method should send data encoded as json
+ (default False)
+ :type json_encoded: bool
:rtype: dict
"""
@@ -261,7 +270,7 @@ class Twython(EndpointsMixin, object):
url = '%s/%s.json' % (self.api_url % version, endpoint)
content = self._request(url, method=method, params=params,
- api_call=url)
+ api_call=url, json_encoded=json_encoded)
return content
@@ -269,9 +278,13 @@ class Twython(EndpointsMixin, object):
"""Shortcut for GET requests via :class:`request`"""
return self.request(endpoint, params=params, version=version)
- def post(self, endpoint, params=None, version='1.1'):
+ def post(self, endpoint, params=None, version='1.1', json_encoded=False):
"""Shortcut for POST requests via :class:`request`"""
- return self.request(endpoint, 'POST', params=params, version=version)
+ return self.request(endpoint, 'POST', params=params, version=version, json_encoded=json_encoded)
+
+ def delete(self, endpoint, params=None, version='1.1', json_encoded=False):
+ """Shortcut for delete requests via :class:`request`"""
+ return self.request(endpoint, 'DELETE', params=params, version=version, json_encoded=json_encoded)
def get_lastfunction_header(self, header, default_return_value=None):
"""Returns a specific header from the last API call
@@ -418,7 +431,7 @@ class Twython(EndpointsMixin, object):
@staticmethod
def construct_api_url(api_url, **params):
- """Construct a Twitter API url, encoded, with parameters
+ r"""Construct a Twitter API url, encoded, with parameters
:param api_url: URL of the Twitter API endpoint you are attempting
to construct
@@ -457,7 +470,7 @@ class Twython(EndpointsMixin, object):
return self.cursor(self.search, q=search_query, **params)
def cursor(self, function, return_pages=False, **params):
- """Returns a generator for results that match a specified query.
+ r"""Returns a generator for results that match a specified query.
:param function: Instance of a Twython function
(Twython.get_home_timeline, Twython.search)
@@ -489,7 +502,7 @@ class Twython(EndpointsMixin, object):
content = function(**params)
if not content:
- raise StopIteration
+ return
if hasattr(function, 'iter_key'):
results = content.get(function.iter_key)
@@ -504,7 +517,7 @@ class Twython(EndpointsMixin, object):
if function.iter_mode == 'cursor' and \
content['next_cursor_str'] == '0':
- raise StopIteration
+ return
try:
if function.iter_mode == 'id':
@@ -517,7 +530,7 @@ class Twython(EndpointsMixin, object):
params = dict(parse_qsl(next_results.query))
else:
# No more results
- raise StopIteration
+ return
else:
# Twitter gives tweets in reverse chronological order:
params['max_id'] = str(int(content[-1]['id_str']) - 1)
diff --git a/twython/endpoints.py b/twython/endpoints.py
index 00392b8..e35088d 100644
--- a/twython/endpoints.py
+++ b/twython/endpoints.py
@@ -268,7 +268,7 @@ class EndpointsMixin(object):
https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/get-statuses-oembed
"""
- return self.get('statuses/oembed', params=params)
+ return self.get('oembed', params=params)
def get_retweeters_ids(self, **params):
"""Returns a collection of up to 100 user IDs belonging to users who
@@ -300,10 +300,10 @@ class EndpointsMixin(object):
"""Returns the 20 most recent direct messages sent to the authenticating user.
Docs:
- https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/get-messages
+ https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/list-events
"""
- return self.get('direct_messages', params=params)
+ return self.get('direct_messages/events/list', params=params)
get_direct_messages.iter_mode = 'id'
def get_sent_messages(self, **params):
@@ -320,29 +320,29 @@ class EndpointsMixin(object):
"""Returns a single direct message, specified by an ``id`` parameter.
Docs:
- https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/get-message
+ https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/get-event
"""
- return self.get('direct_messages/show', params=params)
+ return self.get('direct_messages/events/show', params=params)
def destroy_direct_message(self, **params):
"""Destroys the direct message specified in the required ``id`` parameter
Docs:
- https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/delete-message
+ https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/delete-message-event
"""
- return self.post('direct_messages/destroy', params=params)
+ return self.delete('direct_messages/events/destroy', params=params)
def send_direct_message(self, **params):
"""Sends a new direct message to the specified user from the
authenticating user.
Docs:
- https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/new-message
+ https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/new-event
"""
- return self.post('direct_messages/new', params=params)
+ return self.post('direct_messages/events/new', params=params, json_encoded=True)
# Friends & Followers
def get_user_ids_of_blocked_retweets(self, **params):
diff --git a/twython/streaming/api.py b/twython/streaming/api.py
index 0195f38..6073abb 100644
--- a/twython/streaming/api.py
+++ b/twython/streaming/api.py
@@ -86,8 +86,6 @@ class TwythonStreamer(object):
# Set up type methods
StreamTypes = TwythonStreamerTypes(self)
self.statuses = StreamTypes.statuses
- self.user = StreamTypes.user
- self.site = StreamTypes.site
self.connected = False
@@ -125,7 +123,7 @@ class TwythonStreamer(object):
self.on_timeout()
else:
if response.status_code != 200:
- self.on_error(response.status_code, response.content)
+ self.on_error(response.status_code, response.content, response.headers)
if self.retry_count and \
(self.retry_count - retry_counter) > 0:
@@ -178,7 +176,7 @@ class TwythonStreamer(object):
"""
return True
- def on_error(self, status_code, data): # pragma: no cover
+ def on_error(self, status_code, data, headers=None): # pragma: no cover
"""Called when stream returns non-200 status code
Feel free to override this to handle your streaming data how you
@@ -189,6 +187,9 @@ class TwythonStreamer(object):
:param data: Error message sent from stream
:type data: dict
+
+ :param headers: Response headers sent from the stream (i.e. Retry-After)
+ :type headers: dict
"""
return
diff --git a/twython/streaming/types.py b/twython/streaming/types.py
index 81c5c07..5042d29 100644
--- a/twython/streaming/types.py
+++ b/twython/streaming/types.py
@@ -20,26 +20,6 @@ class TwythonStreamerTypes(object):
self.streamer = streamer
self.statuses = TwythonStreamerTypesStatuses(streamer)
- def user(self, **params):
- """Stream user
-
- Accepted params found at:
- https://dev.twitter.com/docs/api/1.1/get/user
- """
- url = 'https://userstream.twitter.com/%s/user.json' \
- % self.streamer.api_version
- self.streamer._request(url, params=params)
-
- def site(self, **params):
- """Stream site
-
- Accepted params found at:
- https://dev.twitter.com/docs/api/1.1/get/site
- """
- url = 'https://sitestream.twitter.com/%s/site.json' \
- % self.streamer.api_version
- self.streamer._request(url, params=params)
-
class TwythonStreamerTypesStatuses(object):
"""Class for different statuses endpoints
@@ -55,7 +35,7 @@ class TwythonStreamerTypesStatuses(object):
self.params = None
def filter(self, **params):
- """Stream statuses/filter
+ r"""Stream statuses/filter
:param \*\*params: Parameters to send with your stream request
@@ -67,7 +47,7 @@ class TwythonStreamerTypesStatuses(object):
self.streamer._request(url, 'POST', params=params)
def sample(self, **params):
- """Stream statuses/sample
+ r"""Stream statuses/sample
:param \*\*params: Parameters to send with your stream request
@@ -79,7 +59,7 @@ class TwythonStreamerTypesStatuses(object):
self.streamer._request(url, params=params)
def firehose(self, **params):
- """Stream statuses/firehose
+ r"""Stream statuses/firehose
:param \*\*params: Parameters to send with your stream request
@@ -91,7 +71,7 @@ class TwythonStreamerTypesStatuses(object):
self.streamer._request(url, params=params)
def set_dynamic_filter(self, **params):
- """Set/update statuses/filter
+ r"""Set/update statuses/filter
:param \*\*params: Parameters to send with your stream request