Permalink
Please sign in to comment.
Browse files
Better unicode support across API.
Model repr functions should work for py27 (using backslash escaped strings in JSON representation and in __repr__ methods). Also fixed an issue with API not encoding parameters properly for building URLs.
- Loading branch information...
Showing
with
189 additions
and 54 deletions.
- +6 −1 Makefile
- +5 −1 pytest.ini
- +1 −1 setup.cfg
- +1 −0 testdata/get_trends_current_unicode.json
- +45 −15 tests/test_models.py
- +106 −0 tests/test_unicode.py
- +9 −0 tox.ini
- +3 −26 twitter/api.py
- +13 −10 twitter/models.py
7
Makefile
6
pytest.ini
| @@ -1,2 +1,6 @@ | ||
| [pytest] | ||
| -norecursedirs = venv | ||
| +norecursedirs= | ||
| + venv | ||
| + */python?.?/* | ||
| + */site-packages/* | ||
| + .tox/* |
2
setup.cfg
1
testdata/get_trends_current_unicode.json
| @@ -0,0 +1 @@ | ||
| +[{"created_at": "2016-04-12T02:06:56Z", "locations": [{"woeid": 1, "name": "Worldwide"}], "as_of": "2016-04-12T02:11:22Z", "trends": [{"query": "%23LiberdadeLiberdade", "name": "#LiberdadeLiberdade", "promoted_content": null, "url": "http://twitter.com/search?q=%23LiberdadeLiberdade", "tweet_volume": 23755}, {"query": "%23EducandoANina", "name": "#EducandoANina", "promoted_content": null, "url": "http://twitter.com/search?q=%23EducandoANina", "tweet_volume": 22531}, {"query": "%23LHHATL", "name": "#LHHATL", "promoted_content": null, "url": "http://twitter.com/search?q=%23LHHATL", "tweet_volume": 100256}, {"query": "%23ReapareceCFK", "name": "#ReapareceCFK", "promoted_content": null, "url": "http://twitter.com/search?q=%23ReapareceCFK", "tweet_volume": 99254}, {"query": "%23LuanACaixa", "name": "#LuanACaixa", "promoted_content": null, "url": "http://twitter.com/search?q=%23LuanACaixa", "tweet_volume": 128806}, {"query": "%22THANK+YOU+ARDEN%22", "name": "THANK YOU ARDEN", "promoted_content": null, "url": "http://twitter.com/search?q=%22THANK+YOU+ARDEN%22", "tweet_volume": 53636}, {"query": "Tommie", "name": "Tommie", "promoted_content": null, "url": "http://twitter.com/search?q=Tommie", "tweet_volume": 36338}, {"query": "Temer", "name": "Temer", "promoted_content": null, "url": "http://twitter.com/search?q=Temer", "tweet_volume": 103566}, {"query": "%22Josh+Gordon%22", "name": "Josh Gordon", "promoted_content": null, "url": "http://twitter.com/search?q=%22Josh+Gordon%22", "tweet_volume": 56436}, {"query": "%22Roberto+Jefferson%22", "name": "Roberto Jefferson", "promoted_content": null, "url": "http://twitter.com/search?q=%22Roberto+Jefferson%22", "tweet_volume": null}, {"query": "Cesaro", "name": "Cesaro", "promoted_content": null, "url": "http://twitter.com/search?q=Cesaro", "tweet_volume": 12000}, {"query": "%22Josh+Jackson%22", "name": "Josh Jackson", "promoted_content": null, "url": "http://twitter.com/search?q=%22Josh+Jackson%22", "tweet_volume": 15524}, {"query": "%22Esteban+Lamothe%22", "name": "Esteban Lamothe", "promoted_content": null, "url": "http://twitter.com/search?q=%22Esteban+Lamothe%22", "tweet_volume": null}, {"query": "%22LUCERO+CANTA+NO+BRASIL%22", "name": "LUCERO CANTA NO BRASIL", "promoted_content": null, "url": "http://twitter.com/search?q=%22LUCERO+CANTA+NO+BRASIL%22", "tweet_volume": 35999}, {"query": "Poncho", "name": "Poncho", "promoted_content": null, "url": "http://twitter.com/search?q=Poncho", "tweet_volume": 29235}, {"query": "%23VelhoChico", "name": "#VelhoChico", "promoted_content": null, "url": "http://twitter.com/search?q=%23VelhoChico", "tweet_volume": 14674}, {"query": "%23DWTS", "name": "#DWTS", "promoted_content": null, "url": "http://twitter.com/search?q=%23DWTS", "tweet_volume": 28444}, {"query": "%23MiMejorMomento", "name": "#MiMejorMomento", "promoted_content": null, "url": "http://twitter.com/search?q=%23MiMejorMomento", "tweet_volume": 11922}, {"query": "%23N%C3%A3oD%C3%AAUnfTagueirosSdv", "name": "#N\u00e3oD\u00eaUnfTagueirosSdv", "promoted_content": null, "url": "http://twitter.com/search?q=%23N%C3%A3oD%C3%AAUnfTagueirosSdv", "tweet_volume": 16014}, {"query": "%23BlackInkCrew", "name": "#BlackInkCrew", "promoted_content": null, "url": "http://twitter.com/search?q=%23BlackInkCrew", "tweet_volume": 13828}, {"query": "%23BulletClub", "name": "#BulletClub", "promoted_content": null, "url": "http://twitter.com/search?q=%23BulletClub", "tweet_volume": null}, {"query": "%23VzlaApoyaALaAN", "name": "#VzlaApoyaALaAN", "promoted_content": null, "url": "http://twitter.com/search?q=%23VzlaApoyaALaAN", "tweet_volume": 26402}, {"query": "%23WelcomeToBrazilPurposeTour", "name": "#WelcomeToBrazilPurposeTour", "promoted_content": null, "url": "http://twitter.com/search?q=%23WelcomeToBrazilPurposeTour", "tweet_volume": 11638}, {"query": "%23RAWCL", "name": "#RAWCL", "promoted_content": null, "url": "http://twitter.com/search?q=%23RAWCL", "tweet_volume": null}, {"query": "%23MemesDeEx", "name": "#MemesDeEx", "promoted_content": null, "url": "http://twitter.com/search?q=%23MemesDeEx", "tweet_volume": null}, {"query": "%23HereIAm", "name": "#HereIAm", "promoted_content": null, "url": "http://twitter.com/search?q=%23HereIAm", "tweet_volume": null}, {"query": "%23VoicePlayoffs", "name": "#VoicePlayoffs", "promoted_content": null, "url": "http://twitter.com/search?q=%23VoicePlayoffs", "tweet_volume": 26373}, {"query": "%23%D8%A7%D9%82%D9%88%D9%8A_%D8%B9%D8%A8%D8%A7%D8%B1%D9%87_%D8%B1%D9%88%D9%85%D8%A7%D9%86%D8%B3%D9%8A%D9%87", "name": "#\u0627\u0642\u0648\u064a_\u0639\u0628\u0627\u0631\u0647_\u0631\u0648\u0645\u0627\u0646\u0633\u064a\u0647", "promoted_content": null, "url": "http://twitter.com/search?q=%23%D8%A7%D9%82%D9%88%D9%8A_%D8%B9%D8%A8%D8%A7%D8%B1%D9%87_%D8%B1%D9%88%D9%85%D8%A7%D9%86%D8%B3%D9%8A%D9%87", "tweet_volume": 36470}, {"query": "%23SouthernCharm", "name": "#SouthernCharm", "promoted_content": null, "url": "http://twitter.com/search?q=%23SouthernCharm", "tweet_volume": null}, {"query": "%23JackieRobinsonPBS", "name": "#JackieRobinsonPBS", "promoted_content": null, "url": "http://twitter.com/search?q=%23JackieRobinsonPBS", "tweet_volume": null}, {"query": "%23Gotham", "name": "#Gotham", "promoted_content": null, "url": "http://twitter.com/search?q=%23Gotham", "tweet_volume": 16606}, {"query": "%23%D8%AA%D8%B9%D9%84%D9%8A%D9%82_%D8%A7%D9%84%D8%AF%D8%B1%D8%A7%D8%B3%D9%87", "name": "#\u062a\u0639\u0644\u064a\u0642_\u0627\u0644\u062f\u0631\u0627\u0633\u0647", "promoted_content": null, "url": "http://twitter.com/search?q=%23%D8%AA%D8%B9%D9%84%D9%8A%D9%82_%D8%A7%D9%84%D8%AF%D8%B1%D8%A7%D8%B3%D9%87", "tweet_volume": 52968}, {"query": "%23AhoraElPuntoEs", "name": "#AhoraElPuntoEs", "promoted_content": null, "url": "http://twitter.com/search?q=%23AhoraElPuntoEs", "tweet_volume": 13352}, {"query": "%23TopDanceCasting2", "name": "#TopDanceCasting2", "promoted_content": null, "url": "http://twitter.com/search?q=%23TopDanceCasting2", "tweet_volume": 20494}, {"query": "%23isola", "name": "#isola", "promoted_content": null, "url": "http://twitter.com/search?q=%23isola", "tweet_volume": 80890}, {"query": "%23Supergirl", "name": "#Supergirl", "promoted_content": null, "url": "http://twitter.com/search?q=%23Supergirl", "tweet_volume": 16096}, {"query": "%23BatesMotel", "name": "#BatesMotel", "promoted_content": null, "url": "http://twitter.com/search?q=%23BatesMotel", "tweet_volume": null}, {"query": "%23JaneTheVirgin", "name": "#JaneTheVirgin", "promoted_content": null, "url": "http://twitter.com/search?q=%23JaneTheVirgin", "tweet_volume": null}, {"query": "%23%D9%86%D9%81%D8%B3%D9%83_%D8%AA%D8%A8%D9%8A%D8%B9_%D8%A7%D9%8A%D9%87_%D9%84%D9%84%D8%B3%D8%B9%D9%88%D8%AF%D9%8A%D9%87", "name": "#\u0646\u0641\u0633\u0643_\u062a\u0628\u064a\u0639_\u0627\u064a\u0647_\u0644\u0644\u0633\u0639\u0648\u062f\u064a\u0647", "promoted_content": null, "url": "http://twitter.com/search?q=%23%D9%86%D9%81%D8%B3%D9%83_%D8%AA%D8%A8%D9%8A%D8%B9_%D8%A7%D9%8A%D9%87_%D9%84%D9%84%D8%B3%D8%B9%D9%88%D8%AF%D9%8A%D9%87", "tweet_volume": null}, {"query": "%23AnaPaulaNoMissEMisses", "name": "#AnaPaulaNoMissEMisses", "promoted_content": null, "url": "http://twitter.com/search?q=%23AnaPaulaNoMissEMisses", "tweet_volume": 43926}, {"query": "%23LasMemoriasDeMoises", "name": "#LasMemoriasDeMoises", "promoted_content": null, "url": "http://twitter.com/search?q=%23LasMemoriasDeMoises", "tweet_volume": null}, {"query": "%23YoTengoWaveVIP", "name": "#YoTengoWaveVIP", "promoted_content": null, "url": "http://twitter.com/search?q=%23YoTengoWaveVIP", "tweet_volume": 11276}, {"query": "%23LunesdeMhoni", "name": "#LunesdeMhoni", "promoted_content": null, "url": "http://twitter.com/search?q=%23LunesdeMhoni", "tweet_volume": null}, {"query": "%23%D8%AA%D8%B4%D8%AF_%D8%A7%D9%86%D8%AA%D8%A8%D8%A7%D9%87%D9%8A_%D8%A7%D8%B0%D8%A7", "name": "#\u062a\u0634\u062f_\u0627\u0646\u062a\u0628\u0627\u0647\u064a_\u0627\u0630\u0627", "promoted_content": null, "url": "http://twitter.com/search?q=%23%D8%AA%D8%B4%D8%AF_%D8%A7%D9%86%D8%AA%D8%A8%D8%A7%D9%87%D9%8A_%D8%A7%D8%B0%D8%A7", "tweet_volume": null}, {"query": "%23AmigasFalando", "name": "#AmigasFalando", "promoted_content": null, "url": "http://twitter.com/search?q=%23AmigasFalando", "tweet_volume": null}, {"query": "%23InTheUnlikelyEventOfATie", "name": "#InTheUnlikelyEventOfATie", "promoted_content": null, "url": "http://twitter.com/search?q=%23InTheUnlikelyEventOfATie", "tweet_volume": null}, {"query": "%23ALDUBSummerAdventure", "name": "#ALDUBSummerAdventure", "promoted_content": null, "url": "http://twitter.com/search?q=%23ALDUBSummerAdventure", "tweet_volume": 348878}, {"query": "%23KasichFamily", "name": "#KasichFamily", "promoted_content": null, "url": "http://twitter.com/search?q=%23KasichFamily", "tweet_volume": null}, {"query": "%23CiudadanosCNN", "name": "#CiudadanosCNN", "promoted_content": null, "url": "http://twitter.com/search?q=%23CiudadanosCNN", "tweet_volume": null}, {"query": "%23ChatConLaPandillaEdward", "name": "#ChatConLaPandillaEdward", "promoted_content": null, "url": "http://twitter.com/search?q=%23ChatConLaPandillaEdward", "tweet_volume": null}]}] |
60
tests/test_models.py
106
tests/test_unicode.py
| @@ -0,0 +1,106 @@ | ||
| +# encoding: utf-8 | ||
| + | ||
| +import json | ||
| +import pickle | ||
| +import re | ||
| +import sys | ||
| +import unittest | ||
| +import warnings | ||
| + | ||
| +import responses | ||
| +import twitter | ||
| + | ||
| +warnings.filterwarnings('ignore', category=DeprecationWarning) | ||
| + | ||
| +DEFAULT_URL = re.compile(r'https?://.*\.twitter.com/1\.1/.*') | ||
| + | ||
| +class ErrNull(object): | ||
| + """ Suppress output of tests while writing to stdout or stderr. This just | ||
| + takes in data and does nothing with it. | ||
| + """ | ||
| + | ||
| + def write(self, data): | ||
| + pass | ||
| + | ||
| + | ||
| +class ApiTest(unittest.TestCase): | ||
| + def setUp(self): | ||
| + self.maxDiff = None | ||
| + self.api = twitter.Api( | ||
| + consumer_key='test', | ||
| + consumer_secret='test', | ||
| + access_token_key='test', | ||
| + access_token_secret='test', | ||
| + sleep_on_rate_limit=False) | ||
| + self.base_url = 'https://api.twitter.com/1.1' | ||
| + self._stderr = sys.stderr | ||
| + sys.stderr = ErrNull() | ||
| + | ||
| + def tearDown(self): | ||
| + sys.stderr = self._stderr | ||
| + pass | ||
| + | ||
| + def test_trend_repr1(self): | ||
| + trend = twitter.Trend( | ||
| + name="#نفسك_تبيع_ايه_للسعوديه", | ||
| + url="http://twitter.com/search?q=%23ChangeAConsonantSpoilAMovie", | ||
| + timestamp='whatever') | ||
| + try: | ||
| + trend.__repr__() | ||
| + except Exception as e: | ||
| + self.fail(e) | ||
| + | ||
| + def test_trend_repr2(self): | ||
| + trend = twitter.Trend( | ||
| + name="#N\u00e3oD\u00eaUnfTagueirosSdv", | ||
| + url='http://twitter.com/search?q=%23ChangeAConsonantSpoilAMovie', | ||
| + timestamp='whatever') | ||
| + | ||
| + try: | ||
| + trend.__repr__() | ||
| + except Exception as e: | ||
| + self.fail(e) | ||
| + | ||
| + @responses.activate | ||
| + def test_trend_repr3(self): | ||
| + with open('testdata/get_trends_current_unicode.json', 'r') as f: | ||
| + resp_data = f.read() | ||
| + | ||
| + responses.add( | ||
| + responses.GET, | ||
| + 'https://api.twitter.com/1.1/trends/place.json?id=1', | ||
| + body=resp_data, | ||
| + status=200, | ||
| + match_querystring=True) | ||
| + | ||
| + resp = self.api.GetTrendsCurrent() | ||
| + for r in resp: | ||
| + print(r.__str__()) | ||
| + try: | ||
| + r.__repr__() | ||
| + except Exception as e: | ||
| + self.fail(e) | ||
| + | ||
| + @responses.activate | ||
| + def test_unicode_get_search(self): | ||
| + responses.add(responses.GET, DEFAULT_URL, body=b'{}', status=200) | ||
| + try: | ||
| + self.api.GetSearch(term="#ابشري_قابوس_جاء") | ||
| + except Exception as e: | ||
| + self.fail(e) | ||
| + | ||
| + def test_constructed_status(self): | ||
| + s = twitter.Status() | ||
| + s.text = "可以倒着飞的飞机" | ||
| + s.created_at = "016-02-13T23:00:00" | ||
| + s.screen_name = "himawari8bot" | ||
| + s.id = 1 | ||
| + try: | ||
| + s.__repr__() | ||
| + except Exception as e: | ||
| + self.fail(e) | ||
| + | ||
| + | ||
| +if __name__ == "__main__": | ||
| + suite = unittest.TestLoader().loadTestsFromTestCase(ApiTest) | ||
| + unittest.TextTestRunner(verbosity=2).run(suite) |
9
tox.ini
| @@ -0,0 +1,9 @@ | ||
| +[tox] | ||
| +envlist = py27, py35 | ||
| + | ||
| +[testenv] | ||
| +deps = -rrequirements.testing.txt | ||
| +commands = make test | ||
| +whitelist_externals = /bin/bash | ||
| + make | ||
| + |
29
twitter/api.py
23
twitter/models.py
0 comments on commit
df5f3e8