Update Twython to 3.5.0.

This commit is contained in:
Bill Dengler 2017-07-08 06:47:31 +00:00
parent 6c6784bd86
commit 90410a72fa
3 changed files with 103 additions and 41 deletions

View File

@ -19,7 +19,7 @@ Questions, comments? ryan@venodesigns.net
"""
__author__ = 'Ryan McGrath <ryan@venodesigns.net>'
__version__ = '3.3.0'
__version__ = '3.5.0'
from .api import Twython
from .streaming import TwythonStreamer

View File

@ -140,11 +140,8 @@ class Twython(EndpointsMixin, object):
params = params or {}
func = getattr(self.client, method)
if type(params) is dict:
params, files = _transparent_params(params)
else:
params = params
files = list()
params, files = _transparent_params(params)
requests_args = {}
for k, v in self.client_args.items():
# Maybe this should be set as a class variable and only done once?
@ -202,10 +199,8 @@ class Twython(EndpointsMixin, object):
else:
content = response.json()
except ValueError:
# Send the response as is for working with /media/metadata/create.json.
content = response.content
# raise TwythonError('Response was not valid JSON. \
# Unable to decode.')
raise TwythonError('Response was not valid JSON. \
Unable to decode.')
return content
@ -258,8 +253,10 @@ class Twython(EndpointsMixin, object):
url = endpoint
else:
url = '%s/%s.json' % (self.api_url % version, endpoint)
content = self._request(url, method=method, params=params,
api_call=url)
return content
def get(self, endpoint, params=None, version='1.1'):
@ -531,7 +528,7 @@ class Twython(EndpointsMixin, object):
@staticmethod
def html_for_tweet(tweet, use_display_url=True, use_expanded_url=False, expand_quoted_status=False):
"""Return HTML for a tweet (urls, mentions, hashtags replaced with links)
"""Return HTML for a tweet (urls, mentions, hashtags, symbols replaced with links)
:param tweet: Tweet object from received from Twitter API
:param use_display_url: Use display URL to represent link
@ -547,8 +544,18 @@ class Twython(EndpointsMixin, object):
if 'retweeted_status' in tweet:
tweet = tweet['retweeted_status']
if 'extended_tweet' in tweet:
tweet = tweet['extended_tweet']
orig_tweet_text = tweet.get('full_text') or tweet['text']
display_text_range = tweet.get('display_text_range') or [0, len(orig_tweet_text)]
display_text_start, display_text_end = display_text_range[0], display_text_range[1]
display_text = orig_tweet_text[display_text_start:display_text_end]
prefix_text = orig_tweet_text[0:display_text_start]
suffix_text = orig_tweet_text[display_text_end:len(orig_tweet_text)]
if 'entities' in tweet:
text = tweet['text']
entities = tweet['entities']
# Mentions
@ -556,9 +563,13 @@ class Twython(EndpointsMixin, object):
key=lambda mention: len(mention['screen_name']), reverse=True):
start, end = entity['indices'][0], entity['indices'][1]
mention_html = '<a href="https://twitter.com/%(screen_name)s" class="twython-mention">@%(screen_name)s</a>'
text = re.sub(r'(?<!>)' + tweet['text'][start:end] + '(?!</a>)',
mention_html % {'screen_name': entity['screen_name']}, text)
mention_html = '<a href="https://twitter.com/%(screen_name)s" ' \
'class="twython-mention">@%(screen_name)s</a>' % {'screen_name': entity['screen_name']}
sub_expr = r'(?<!>)' + orig_tweet_text[start:end] + '(?!</a>)'
if display_text_start <= start <= display_text_end:
display_text = re.sub(sub_expr, mention_html, display_text)
else:
prefix_text = re.sub(sub_expr, mention_html, prefix_text)
# Hashtags
for entity in sorted(entities['hashtags'],
@ -566,8 +577,17 @@ class Twython(EndpointsMixin, object):
start, end = entity['indices'][0], entity['indices'][1]
hashtag_html = '<a href="https://twitter.com/search?q=%%23%(hashtag)s" class="twython-hashtag">#%(hashtag)s</a>'
text = re.sub(r'(?<!>)' + tweet['text'][start:end] + '(?!</a>)',
hashtag_html % {'hashtag': entity['text']}, text)
display_text = re.sub(r'(?<!>)' + orig_tweet_text[start:end] + '(?!</a>)',
hashtag_html % {'hashtag': entity['text']}, display_text)
# Symbols
for entity in sorted(entities['symbols'],
key=lambda symbol: len(symbol['text']), reverse=True):
start, end = entity['indices'][0], entity['indices'][1]
symbol_html = '<a href="https://twitter.com/search?q=%%24%(symbol)s" class="twython-symbol">$%(symbol)s</a>'
display_text = re.sub(r'(?<!>)' + re.escape(orig_tweet_text[start:end]) + r'\b(?!</a>)',
symbol_html % {'symbol': entity['text']}, display_text)
# Urls
for entity in entities['urls']:
@ -580,9 +600,11 @@ class Twython(EndpointsMixin, object):
else:
shown_url = entity['url']
url_html = '<a href="%s" class="twython-url">%s</a>'
text = text.replace(tweet['text'][start:end],
url_html % (entity['url'], shown_url))
url_html = '<a href="%s" class="twython-url">%s</a>' % (entity['url'], shown_url)
if display_text_start <= start <= display_text_end:
display_text = display_text.replace(orig_tweet_text[start:end], url_html)
else:
suffix_text = suffix_text.replace(orig_tweet_text[start:end], url_html)
# Media
if 'media' in entities:
@ -596,13 +618,17 @@ class Twython(EndpointsMixin, object):
else:
shown_url = entity['url']
url_html = '<a href="%s" class="twython-media">%s</a>'
text = text.replace(tweet['text'][start:end],
url_html % (entity['url'], shown_url))
url_html = '<a href="%s" class="twython-media">%s</a>' % (entity['url'], shown_url)
if display_text_start <= start <= display_text_end:
# for compatibility with pre-extended tweets
display_text = display_text.replace(orig_tweet_text[start:end], url_html)
else:
suffix_text = suffix_text.replace(orig_tweet_text[start:end], url_html)
if expand_quoted_status and tweet.get('is_quote_status'):
quote_text = ''
if expand_quoted_status and tweet.get('is_quote_status') and tweet.get('quoted_status'):
quoted_status = tweet['quoted_status']
text += '<blockquote class="twython-quote">%(quote)s<cite><a href="%(quote_tweet_link)s">' \
quote_text += '<blockquote class="twython-quote">%(quote)s<cite><a href="%(quote_tweet_link)s">' \
'<span class="twython-quote-user-name">%(quote_user_name)s</span>' \
'<span class="twython-quote-user-screenname">@%(quote_user_screen_name)s</span></a>' \
'</cite></blockquote>' % \
@ -612,4 +638,9 @@ class Twython(EndpointsMixin, object):
'quote_user_name': quoted_status['user']['name'],
'quote_user_screen_name': quoted_status['user']['screen_name']}
return text
return '%(prefix)s%(display)s%(suffix)s%(quote)s' % {
'prefix': '<span class="twython-tweet-prefix">%s</span>' % prefix_text if prefix_text else '',
'display': display_text,
'suffix': '<span class="twython-tweet-suffix">%s</span>' % suffix_text if suffix_text else '',
'quote': quote_text
}

View File

@ -14,13 +14,14 @@ This map is organized the order functions are documented at:
https://dev.twitter.com/docs/api/1.1
"""
import json
import os
import warnings
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from io import BytesIO
from time import sleep
#try:
#from StringIO import StringIO
#except ImportError:
#from io import StringIO
from .advisory import TwythonDeprecationWarning
@ -143,15 +144,13 @@ class EndpointsMixin(object):
Docs:
https://dev.twitter.com/rest/reference/post/media/upload
"""
# https://dev.twitter.com/rest/reference/get/media/upload-status
if params and params.get('command', '') == 'STATUS':
return self.get('https://upload.twitter.com/1.1/media/upload.json', params=params)
return self.post('https://upload.twitter.com/1.1/media/upload.json', params=params)
def set_description(self, **params):
""" Adds a description to an image."""
# This method only accepts strings, no dictionaries.
params = json.dumps(params)
return self.post("media/metadata/create", params=params)
def upload_video(self, media, media_type, size=None):
def upload_video(self, media, media_type, media_category=None, size=None, check_progress=False):
"""Uploads video file to Twitter servers in chunks. The file will be available to be attached
to a status for 60 minutes. To attach to a update, pass a list of returned media ids
to the 'update_status' method using the 'media_ids' param.
@ -176,7 +175,8 @@ class EndpointsMixin(object):
params = {
'command': 'INIT',
'media_type': media_type,
'total_bytes': size
'total_bytes': size,
'media_category': media_category
}
response_init = self.post(upload_url, params=params)
media_id = response_init['media_id']
@ -187,7 +187,7 @@ class EndpointsMixin(object):
data = media.read(1*1024*1024)
if not data:
break
media_chunk = StringIO()
media_chunk = BytesIO()
media_chunk.write(data)
media_chunk.seek(0)
@ -205,7 +205,38 @@ class EndpointsMixin(object):
'command': 'FINALIZE',
'media_id': media_id
}
return self.post(upload_url, params=params)
response = self.post(upload_url, params=params)
# Only get the status if explicity asked to
# Default to False
if check_progress:
# Stage 4: STATUS call if still processing
params = {
'command': 'STATUS',
'media_id': media_id
}
# added code to handle if media_category is NOT set and check_progress=True
# the API will return a NoneType object in this case
try:
processing_state = response.get('processing_info').get('state')
except AttributeError:
return response
if processing_state:
while (processing_state == 'pending' or processing_state == 'in_progress') :
# get the secs to wait
check_after_secs = response.get('processing_info').get('check_after_secs')
if check_after_secs:
sleep(check_after_secs)
response = self.get(upload_url, params=params)
# get new state after waiting
processing_state = response.get('processing_info').get('state')
return response
def get_oembed_tweet(self, **params):
"""Returns information allowing the creation of an embedded