X Tutup
Skip to content

Commit c5e37b1

Browse files
committed
Merge pull request bear#319 from bear/alt-text_issue-316
Alt text - work for issue 316
2 parents 7e6d53b + 824461e commit c5e37b1

File tree

4 files changed

+166
-73
lines changed

4 files changed

+166
-73
lines changed

testdata/get_status_ext_alt.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"place": null, "favorite_count": 43, "possibly_sensitive": false, "retweet_count": 28, "id": 724441953534877696, "in_reply_to_user_id": null, "id_str": "724441953534877696", "coordinates": null, "favorited": false, "contributors": null, "in_reply_to_status_id": null, "text": "\u201cJon Snow is dead.\u201d\u200a\u2014\u200a@HBOPR https://t.co/8IK0pbcxIr https://t.co/jvZ3Koip6x", "user": {"default_profile_image": false, "screen_name": "Medium", "time_zone": "Pacific Time (US & Canada)", "notifications": false, "profile_use_background_image": false, "protected": false, "name": "Medium", "contributors_enabled": false, "followers_count": 1727713, "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", "listed_count": 10639, "profile_link_color": "00AB69", "profile_image_url_https": "https://pbs.twimg.com/profile_images/672905320751083521/DqQkNLfW_normal.png", "id": 571202103, "profile_background_tile": true, "is_translator": false, "id_str": "571202103", "profile_image_url": "http://pbs.twimg.com/profile_images/672905320751083521/DqQkNLfW_normal.png", "following": false, "has_extended_profile": false, "profile_sidebar_border_color": "FFFFFF", "lang": "en", "url": "http://t.co/39nrCKtdRI", "is_translation_enabled": false, "location": "San Francisco, CA, US", "default_profile": false, "entities": {"url": {"urls": [{"url": "http://t.co/39nrCKtdRI", "display_url": "medium.com", "expanded_url": "http://medium.com", "indices": [0, 22]}]}, "description": {"urls": []}}, "verified": true, "created_at": "Fri May 04 20:16:39 +0000 2012", "utc_offset": -25200, "profile_background_color": "FFFFFF", "favourites_count": 4430, "statuses_count": 14794, "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", "profile_banner_url": "https://pbs.twimg.com/profile_banners/571202103/1444267389", "description": "Move thinking forward.", "geo_enabled": false, "follow_request_sent": false, "friends_count": 145, "profile_sidebar_fill_color": "EFEFEF", "profile_text_color": "333333"}, "lang": "en", "truncated": false, "is_quote_status": false, "in_reply_to_status_id_str": null, "entities": {"hashtags": [], "media": [{"id": 724441951391604736, "id_str": "724441951391604736", "media_url": "http://pbs.twimg.com/media/Cg28HdUWYAAvmwn.jpg", "media_url_https": "https://pbs.twimg.com/media/Cg28HdUWYAAvmwn.jpg", "type": "photo", "display_url": "pic.twitter.com/jvZ3Koip6x", "url": "https://t.co/jvZ3Koip6x", "sizes": {"large": {"resize": "fit", "w": 750, "h": 748}, "small": {"resize": "fit", "w": 340, "h": 339}, "thumb": {"resize": "crop", "w": 150, "h": 150}, "medium": {"resize": "fit", "w": 600, "h": 598}}, "expanded_url": "http://twitter.com/Medium/status/724441953534877696/photo/1", "indices": [53, 76]}], "user_mentions": [{"name": "HBO PR", "id": 2153490764, "id_str": "2153490764", "screen_name": "HBOPR", "indices": [22, 28]}], "symbols": [], "urls": [{"url": "https://t.co/8IK0pbcxIr", "display_url": "medium.com/hbo-cinemax-pr\u2026", "expanded_url": "https://medium.com/hbo-cinemax-pr/game-of-thrones-season-6-episodes-53586437cbb3#---0-17.cvym8cqn4", "indices": [29, 52]}]}, "retweeted": false, "in_reply_to_user_id_str": null, "created_at": "Mon Apr 25 03:36:35 +0000 2016", "in_reply_to_screen_name": null, "extended_entities": {"media": [{"id": 724441951391604736, "id_str": "724441951391604736", "media_url": "http://pbs.twimg.com/media/Cg28HdUWYAAvmwn.jpg", "media_url_https": "https://pbs.twimg.com/media/Cg28HdUWYAAvmwn.jpg", "type": "photo", "display_url": "pic.twitter.com/jvZ3Koip6x", "ext_alt_text": "\u201cJon Snow is dead.\u2026\u201d from \u201cGAME OF THRONES SEASON 6 EPISODES\u201d by HBO PR.", "url": "https://t.co/jvZ3Koip6x", "sizes": {"large": {"resize": "fit", "w": 750, "h": 748}, "small": {"resize": "fit", "w": 340, "h": 339}, "thumb": {"resize": "crop", "w": 150, "h": 150}, "medium": {"resize": "fit", "w": 600, "h": 598}}, "expanded_url": "http://twitter.com/Medium/status/724441953534877696/photo/1", "indices": [53, 76]}]}, "possibly_sensitive_appealable": false, "geo": null, "source": "<a href=\"https://medium.com\" rel=\"nofollow\">Medium</a>"}

tests/test_api_30.py

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
# encoding: utf-8
2+
from __future__ import unicode_literals, print_function
23

34
import json
5+
import re
46
import sys
57
import unittest
8+
import warnings
69

710
import twitter
811

9-
import warnings
10-
1112
warnings.filterwarnings('ignore', category=DeprecationWarning)
1213

1314
import responses
1415

16+
DEFAULT_URL = re.compile(r'https?://.*\.twitter.com/1\.1/.*')
17+
1518

1619
class ErrNull(object):
1720
""" Suppress output of tests while writing to stdout or stderr. This just
@@ -571,8 +574,7 @@ def testGetFollowersIDs(self):
571574
resp_data = f.read()
572575
responses.add(
573576
responses.GET,
574-
'{base_url}/followers/ids.json?count=5000&cursor=-1&screen_name=GirlsMakeGames'.format(
575-
base_url=self.api.base_url),
577+
'https://api.twitter.com/1.1/followers/ids.json?cursor=-1&stringify_ids=False&count=5000&screen_name=GirlsMakeGames',
576578
body=resp_data,
577579
match_querystring=True,
578580
status=200)
@@ -582,8 +584,7 @@ def testGetFollowersIDs(self):
582584
resp_data = f.read()
583585
responses.add(
584586
responses.GET,
585-
'{base_url}/followers/ids.json?cursor=1482201362283529597&count=5000&screen_name=GirlsMakeGames'.format(
586-
base_url=self.api.base_url),
587+
'https://api.twitter.com/1.1/followers/ids.json?count=5000&screen_name=GirlsMakeGames&cursor=1482201362283529597&stringify_ids=False',
587588
body=resp_data,
588589
match_querystring=True,
589590
status=200)
@@ -594,9 +595,6 @@ def testGetFollowersIDs(self):
594595
self.assertTrue(type(resp[0]) is int)
595596

596597
# Error checking
597-
self.assertRaises(
598-
twitter.TwitterError,
599-
lambda: self.api.GetFollowerIDs(count='infinity'))
600598
self.assertRaises(
601599
twitter.TwitterError,
602600
lambda: self.api.GetFollowerIDs(total_count='infinity'))
@@ -653,8 +651,7 @@ def testGetFollowerIDsPaged(self):
653651
resp_data = f.read()
654652
responses.add(
655653
responses.GET,
656-
'{base_url}/followers/ids.json?count=5000&stringify_ids=False&screen_name=himawari8bot&cursor=-1'.format(
657-
base_url=self.api.base_url),
654+
'https://api.twitter.com/1.1/followers/ids.json?count=5000&stringify_ids=False&cursor=-1&screen_name=himawari8bot',
658655
body=resp_data,
659656
match_querystring=True,
660657
status=200)
@@ -1440,3 +1437,95 @@ def testLookupFriendshipBlockMute(self):
14401437
resp = self.api.LookupFriendship(screen_name='dickc')
14411438
self.assertEqual(resp[0].muting, True)
14421439
self.assertEqual(resp[0].blocking, True)
1440+
1441+
@responses.activate
1442+
def testPostMediaMetadata(self):
1443+
responses.add(
1444+
responses.POST,
1445+
'https://upload.twitter.com/1.1/media/metadata/create.json',
1446+
body=b'',
1447+
status=200)
1448+
resp = self.api.PostMediaMetadata(media_id=718561981427396608, alt_text='test')
1449+
1450+
# At the moment, all we can do is test if the method call works. The response
1451+
# body should be blank, with a 200 status on success.
1452+
self.assertTrue(resp)
1453+
1454+
@responses.activate
1455+
def testGetStatusWithExtAltText(self):
1456+
with open('testdata/get_status_ext_alt.json') as f:
1457+
resp_data = f.read()
1458+
responses.add(responses.GET, DEFAULT_URL, body=resp_data, status=200)
1459+
resp = self.api.GetStatus(status_id=724441953534877696)
1460+
self.assertEqual(resp.media[0].ext_alt_text, "\u201cJon Snow is dead.\u2026\u201d from \u201cGAME OF THRONES SEASON 6 EPISODES\u201d by HBO PR.")
1461+
1462+
1463+
@responses.activate
1464+
def testGetStatus(self):
1465+
with open('testdata/get_status.json') as f:
1466+
resp_data = f.read()
1467+
responses.add(
1468+
responses.GET,
1469+
'https://api.twitter.com/1.1/statuses/show.json?include_entities=True&include_my_retweet=True&include_ext_alt_text=True&id=397',
1470+
body=resp_data,
1471+
match_querystring=True,
1472+
status=200)
1473+
resp = self.api.GetStatus(status_id=397)
1474+
1475+
self.assertTrue(type(resp) is twitter.Status)
1476+
self.assertEqual(resp.id, 397)
1477+
self.assertEqual(resp.user.id, 12)
1478+
self.assertFalse(resp != resp)
1479+
1480+
self.assertRaises(
1481+
twitter.TwitterError,
1482+
lambda: self.api.GetStatus(status_id='test'))
1483+
1484+
@responses.activate
1485+
def testGetStatusExtraParams(self):
1486+
with open('testdata/get_status_extra_params.json') as f:
1487+
resp_data = f.read()
1488+
responses.add(
1489+
responses.GET,
1490+
'https://api.twitter.com/1.1/statuses/show.json?include_ext_alt_text=True&id=397&include_my_retweet=True&trim_user=True',
1491+
body=resp_data,
1492+
match_querystring=True,
1493+
status=200)
1494+
resp = self.api.GetStatus(status_id=397, trim_user=True, include_entities=False)
1495+
self.assertFalse(resp.user.screen_name)
1496+
1497+
1498+
@responses.activate
1499+
def testGetStatusOembed(self):
1500+
with open('testdata/get_status_oembed.json') as f:
1501+
resp_data = f.read()
1502+
responses.add(
1503+
responses.GET,
1504+
'https://api.twitter.com/1.1/statuses/oembed.json?id=397',
1505+
body=resp_data,
1506+
match_querystring=True,
1507+
status=200)
1508+
responses.add(
1509+
responses.GET,
1510+
'https://api.twitter.com/1.1/statuses/oembed.json?url=https://twitter.com/jack/statuses/397',
1511+
body=resp_data,
1512+
match_querystring=True,
1513+
status=200)
1514+
resp_id = self.api.GetStatusOembed(status_id=397)
1515+
self.assertEqual(resp_id['url'], 'https://twitter.com/jack/statuses/397')
1516+
self.assertEqual(resp_id['provider_url'], 'https://twitter.com')
1517+
self.assertEqual(resp_id['provider_name'], 'Twitter')
1518+
1519+
self.assertRaises(
1520+
twitter.TwitterError,
1521+
lambda: self.api.GetStatusOembed(status_id='test'))
1522+
1523+
resp_url = self.api.GetStatusOembed(url="https://twitter.com/jack/statuses/397")
1524+
self.assertEqual(resp_id, resp_url)
1525+
1526+
self.assertRaises(
1527+
twitter.TwitterError,
1528+
lambda: self.api.GetStatusOembed(status_id=None, url=None))
1529+
self.assertRaises(
1530+
twitter.TwitterError,
1531+
lambda: self.api.GetStatusOembed(status_id=397, align='test'))

twitter/api.py

Lines changed: 64 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,8 @@ def GetStatus(self,
721721
status_id,
722722
trim_user=False,
723723
include_my_retweet=True,
724-
include_entities=True):
724+
include_entities=True,
725+
include_ext_alt_text=True):
725726
"""Returns a single status message, specified by the status_id parameter.
726727
727728
Args:
@@ -754,11 +755,13 @@ def GetStatus(self,
754755
raise TwitterError({'message': "'status_id' must be an integer."})
755756

756757
if trim_user:
757-
parameters['trim_user'] = 1
758+
parameters['trim_user'] = True
758759
if include_my_retweet:
759-
parameters['include_my_retweet'] = 1
760-
if not include_entities:
761-
parameters['include_entities'] = 'none'
760+
parameters['include_my_retweet'] = True
761+
if include_entities:
762+
parameters['include_entities'] = True
763+
if include_ext_alt_text:
764+
parameters['include_ext_alt_text'] = True
762765

763766
resp = self._RequestUrl(url, 'GET', data=parameters)
764767
data = self._ParseAndCheckTwitter(resp.content.decode('utf-8'))
@@ -1053,6 +1056,29 @@ def UploadMediaSimple(self,
10531056
except KeyError:
10541057
raise TwitterError({'message': 'Media could not be uploaded.'})
10551058

1059+
def PostMediaMetadata(self,
1060+
media_id,
1061+
alt_text=None):
1062+
"""Provide addtional data for uploaded media.
1063+
1064+
Args:
1065+
media_id:
1066+
ID of a previously uploaded media item.
1067+
alt_text:
1068+
Image Alternate Text.
1069+
"""
1070+
url = '%s/media/metadata/create.json' % self.upload_url
1071+
parameters = {}
1072+
1073+
parameters['media_id'] = media_id
1074+
1075+
if alt_text:
1076+
parameters['alt_text'] = {"text": alt_text}
1077+
1078+
resp = self._RequestUrl(url, 'POST', json=parameters)
1079+
1080+
return resp
1081+
10561082
def UploadMediaChunked(self,
10571083
media,
10581084
additional_owners=None,
@@ -1864,12 +1890,12 @@ def GetFollowerIDsPaged(self,
18641890
one for each follower
18651891
"""
18661892
url = '%s/followers/ids.json' % self.base_url
1867-
return self._GetIDsPaged(url,
1868-
user_id,
1869-
screen_name,
1870-
cursor,
1871-
stringify_ids,
1872-
count)
1893+
return self._GetIDsPaged(url=url,
1894+
user_id=user_id,
1895+
screen_name=screen_name,
1896+
cursor=cursor,
1897+
stringify_ids=stringify_ids,
1898+
count=count)
18731899

18741900
def GetFriendIDsPaged(self,
18751901
user_id=None,
@@ -1922,22 +1948,12 @@ def _GetFriendFollowerIDs(self,
19221948
total_count=None):
19231949
""" Common method for GetFriendIDs and GetFollowerIDs """
19241950

1925-
if cursor is not None or count is not None:
1926-
warnings.warn(
1927-
"Use of 'cursor' and 'count' parameters are deprecated as of "
1928-
"python-twitter 3.0. Please use GetFriendIDsPaged or "
1929-
"GetFollowerIDsPaged instead.",
1930-
DeprecationWarning, stacklevel=2)
1931-
19321951
count = 5000
19331952
cursor = -1
19341953
result = []
19351954

19361955
if total_count:
1937-
try:
1938-
total_count = int(total_count)
1939-
except ValueError:
1940-
raise TwitterError({'message': "total_count must be an integer"})
1956+
total_count = enf_type('total_count', int, total_count)
19411957

19421958
if total_count and total_count < count:
19431959
count = total_count
@@ -1947,12 +1963,12 @@ def _GetFriendFollowerIDs(self,
19471963
break
19481964

19491965
next_cursor, previous_cursor, data = self._GetIDsPaged(
1950-
url,
1951-
user_id,
1952-
screen_name,
1953-
cursor,
1954-
stringify_ids,
1955-
count)
1966+
url=url,
1967+
user_id=user_id,
1968+
screen_name=screen_name,
1969+
cursor=cursor,
1970+
stringify_ids=stringify_ids,
1971+
count=count)
19561972

19571973
result.extend([x for x in data])
19581974

@@ -1998,13 +2014,13 @@ def GetFollowerIDs(self,
19982014
A list of integers, one for each user id.
19992015
"""
20002016
url = '%s/followers/ids.json' % self.base_url
2001-
return self._GetFriendFollowerIDs(url,
2002-
user_id,
2003-
screen_name,
2004-
cursor,
2005-
stringify_ids,
2006-
count,
2007-
total_count)
2017+
return self._GetFriendFollowerIDs(url=url,
2018+
user_id=user_id,
2019+
screen_name=screen_name,
2020+
cursor=cursor,
2021+
stringify_ids=stringify_ids,
2022+
count=count,
2023+
total_count=total_count)
20082024

20092025
def GetFriendIDs(self,
20102026
user_id=None,
@@ -4432,7 +4448,7 @@ def _RequestChunkedUpload(self, url, headers, data):
44324448
except requests.RequestException as e:
44334449
raise TwitterError(str(e))
44344450

4435-
def _RequestUrl(self, url, verb, data=None):
4451+
def _RequestUrl(self, url, verb, data=None, json=None):
44364452
"""Request a url.
44374453
44384454
Args:
@@ -4460,36 +4476,22 @@ def _RequestUrl(self, url, verb, data=None):
44604476
pass
44614477

44624478
if verb == 'POST':
4463-
if 'media_ids' in data:
4464-
url = self._BuildUrl(url, extra_params={'media_ids': data['media_ids']})
4465-
4466-
if 'media' in data:
4467-
try:
4468-
resp = requests.post(url,
4469-
files=data,
4470-
auth=self.__auth,
4471-
timeout=self._timeout)
4472-
except requests.RequestException as e:
4473-
raise TwitterError(str(e))
4479+
if data:
4480+
if 'media_ids' in data:
4481+
url = self._BuildUrl(url, extra_params={'media_ids': data['media_ids']})
4482+
resp = requests.post(url, data=data, auth=self.__auth, timeout=self._timeout)
4483+
elif 'media' in data:
4484+
resp = requests.post(url, files=data, auth=self.__auth, timeout=self._timeout)
4485+
else:
4486+
resp = requests.post(url, data=data, auth=self.__auth, timeout=self._timeout)
4487+
elif json:
4488+
resp = requests.post(url, json=json, auth=self.__auth, timeout=self._timeout)
44744489
else:
4475-
try:
4476-
resp = requests.post(url,
4477-
data=data,
4478-
auth=self.__auth,
4479-
timeout=self._timeout)
4480-
4481-
except requests.RequestException as e:
4482-
raise TwitterError(str(e))
4490+
resp = 0 # POST request, but without data or json
44834491

44844492
elif verb == 'GET':
44854493
url = self._BuildUrl(url, extra_params=data)
4486-
try:
4487-
resp = requests.get(url,
4488-
auth=self.__auth,
4489-
timeout=self._timeout)
4490-
4491-
except requests.RequestException as e:
4492-
raise TwitterError(str(e))
4494+
resp = requests.get(url, auth=self.__auth, timeout=self._timeout)
44934495

44944496
else:
44954497
resp = 0 # if not a POST or GET request

twitter/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def __init__(self, **kwargs):
9393
self.param_defaults = {
9494
'display_url': None,
9595
'expanded_url': None,
96+
'ext_alt_text': None,
9697
'id': None,
9798
'media_url': None,
9899
'media_url_https': None,

0 commit comments

Comments
 (0)
X Tutup