Merge lp:~facundo/ubuntu-rest-scopes/corrupt-reddit-response into lp:ubuntu-rest-scopes

Proposed by Facundo Batista
Status: Merged
Approved by: Facundo Batista
Approved revision: 529
Merged at revision: 529
Proposed branch: lp:~facundo/ubuntu-rest-scopes/corrupt-reddit-response
Merge into: lp:ubuntu-rest-scopes
Diff against target: 187 lines (+43/-22)
2 files modified
src/scopes/reddit.py (+19/-10)
src/scopes/tests/test_reddit.py (+24/-12)
To merge this branch: bzr merge lp:~facundo/ubuntu-rest-scopes/corrupt-reddit-response
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Review via email: mp+299700@code.launchpad.net

Commit message

Support corrupt/broken responses from reddit.

Description of the change

Support corrupt/broken responses from reddit.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

Code LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/scopes/reddit.py'
--- src/scopes/reddit.py 2016-06-07 14:20:44 +0000
+++ src/scopes/reddit.py 2016-07-11 14:30:27 +0000
@@ -1,10 +1,11 @@
1# Copyright 2014-2015 Canonical1# Copyright 2014-2016 Canonical
2# All Rights Reserved2# All Rights Reserved
33
4"""The Reddit scope."""4"""The Reddit scope."""
55
6import urllib6import urllib
7import json7import json
8import logging
89
9from ..scopes import SimpleApp10from ..scopes import SimpleApp
10from ..translator import translator11from ..translator import translator
@@ -13,6 +14,8 @@
13# this module is generated by a "make build"14# this module is generated by a "make build"
14from versioninfo import version_info15from versioninfo import version_info
1516
17logger = logging.getLogger("restscopes.reddit")
18
1619
17# the different URIs to get info from Reddit20# the different URIs to get info from Reddit
18POPULAR_SUBREDDITS = 'http://www.reddit.com/subreddits/popular.json?limit=50'21POPULAR_SUBREDDITS = 'http://www.reddit.com/subreddits/popular.json?limit=50'
@@ -176,9 +179,15 @@
176 def _get_results(self, url):179 def _get_results(self, url):
177 """Call the Reddit API."""180 """Call the Reddit API."""
178 hdr = {'User-Agent': "Reddit Ubuntu Rest Scope " + str(version_info['revno'])}181 hdr = {'User-Agent': "Reddit Ubuntu Rest Scope " + str(version_info['revno'])}
179 response = self.urlread(url, headers=hdr)182 try:
180 results = json.loads(response.decode('utf-8'))183 response = self.urlread(url, headers=hdr)
181 return results184 results = json.loads(response.decode('utf-8'))
185 except Exception as err:
186 logger.debug("Error %s(%s) when hitting: %r", err.__class__.__name__, err, url)
187 return []
188
189 children = results['data']['children']
190 return children
182191
183 def _get_available_departments(self, department, locale):192 def _get_available_departments(self, department, locale):
184 """Use popular subreddits to build the departments list."""193 """Use popular subreddits to build the departments list."""
@@ -188,7 +197,7 @@
188 current['label'] = translator('Front page', locale)197 current['label'] = translator('Front page', locale)
189 current['has_subdepartments'] = True198 current['has_subdepartments'] = True
190 # Departments for reddit are a flat list, always display them199 # Departments for reddit are a flat list, always display them
191 for r in self._get_results(POPULAR_SUBREDDITS)['data']['children']:200 for r in self._get_results(POPULAR_SUBREDDITS):
192 sub = {}201 sub = {}
193 name = r['data']['display_name']202 name = r['data']['display_name']
194 sub['label'] = name203 sub['label'] = name
@@ -221,14 +230,14 @@
221 uri = SURFACING_DEP_URI % dict(dep=department, limit=limit)230 uri = SURFACING_DEP_URI % dict(dep=department, limit=limit)
222 yield dict(category=localized_category(THREAD_CATEGORY,231 yield dict(category=localized_category(THREAD_CATEGORY,
223 locale))232 locale))
224 for post in self._get_results(uri)['data']['children']:233 for post in self._get_results(uri):
225 yield dict(result=_parse_post(post, query,234 yield dict(result=_parse_post(post, query,
226 locale, platform))235 locale, platform))
227 else:236 else:
228 uri = SURFACING_URI % dict(limit=limit)237 uri = SURFACING_URI % dict(limit=limit)
229 yield dict(category=localized_category(THREAD_CATEGORY,238 yield dict(category=localized_category(THREAD_CATEGORY,
230 locale))239 locale))
231 for post in self._get_results(uri)['data']['children']:240 for post in self._get_results(uri):
232 yield dict(result=_parse_post(post, query,241 yield dict(result=_parse_post(post, query,
233 locale, platform))242 locale, platform))
234 else:243 else:
@@ -238,14 +247,14 @@
238 subs_uri = SUBREDDIT_SEARCH_URI % dict(query=query_cleaned,247 subs_uri = SUBREDDIT_SEARCH_URI % dict(query=query_cleaned,
239 limit=limit)248 limit=limit)
240 yield dict(category=localized_category(SUBREDDIT_CATEGORY, locale))249 yield dict(category=localized_category(SUBREDDIT_CATEGORY, locale))
241 for subreddit in self._get_results(subs_uri)['data']['children']:250 for subreddit in self._get_results(subs_uri):
242 yield dict(result=_parse_subreddit(subreddit, query, locale))251 yield dict(result=_parse_subreddit(subreddit, query, locale))
243252
244 # Search posts253 # Search posts
245 posts_uri = POST_SEARCH_URI % dict(query=query_cleaned,254 posts_uri = POST_SEARCH_URI % dict(query=query_cleaned,
246 limit=limit)255 limit=limit)
247 yield dict(category=localized_category(S_THREAD_CATEGORY, locale))256 yield dict(category=localized_category(S_THREAD_CATEGORY, locale))
248 for post in self._get_results(posts_uri)['data']['children']:257 for post in self._get_results(posts_uri):
249 yield dict(result=_parse_post(post, query,258 yield dict(result=_parse_post(post, query,
250 locale, platform))259 locale, platform))
251260
@@ -301,7 +310,7 @@
301 'id': 'image',310 'id': 'image',
302 'type': 'image',311 'type': 'image',
303 'source': result['art'],312 'source': result['art'],
304 })313 })
305 # create a 'header' widget314 # create a 'header' widget
306 header = dict(widget={315 header = dict(widget={
307 'id': 'header',316 'id': 'header',
308317
=== modified file 'src/scopes/tests/test_reddit.py'
--- src/scopes/tests/test_reddit.py 2016-03-14 07:51:29 +0000
+++ src/scopes/tests/test_reddit.py 2016-07-11 14:30:27 +0000
@@ -1,6 +1,6 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
22
3# Copyright 2014-2015 Canonical3# Copyright 2014-2016 Canonical
4# All Rights Reserved4# All Rights Reserved
55
6"""Tests for the Simple App base stuff."""6"""Tests for the Simple App base stuff."""
@@ -11,7 +11,7 @@
11from mock import patch11from mock import patch
1212
13from src.scopes import reddit13from src.scopes import reddit
14from src.scopes.tests import get_fixture14from src.scopes.tests import get_fixture, SetupLogChecker
1515
16from versioninfo import version_info16from versioninfo import version_info
1717
@@ -22,22 +22,34 @@
22class SearchTestCase(TestCase):22class SearchTestCase(TestCase):
23 """Tests for the search function."""23 """Tests for the search function."""
2424
25 def test_result_retrieval(self):25 def setUp(self):
26 super(SearchTestCase, self).setUp()
27 SetupLogChecker(self, 'restscopes')
28
29 def test_result_retrieval_ok(self):
26 app = reddit.App(CONFIG)30 app = reddit.App(CONFIG)
27 fake_data = {'test': 123}31 fake_data = {'data': {'children': ['test1', 'test2']}}
28 with patch.object(app, 'urlread') as mock_urlread:32 with patch.object(app, 'urlread') as mock_urlread:
29 mock_urlread.return_value = json.dumps(fake_data)33 mock_urlread.return_value = json.dumps(fake_data)
30 resp = app._get_results('test url')34 resp = app._get_results('test url')
31 self.assertEqual(resp, fake_data)35 self.assertEqual(resp, ['test1', 'test2'])
32 ua = "Reddit Ubuntu Rest Scope " + str(version_info['revno'])36 ua = "Reddit Ubuntu Rest Scope " + str(version_info['revno'])
33 mock_urlread.assert_called_with('test url', headers={'User-Agent': ua})37 mock_urlread.assert_called_with('test url', headers={'User-Agent': ua})
3438
39 def test_result_retrieval_broken(self):
40 app = reddit.App(CONFIG)
41 with patch.object(app, 'urlread') as mock_urlread:
42 mock_urlread.return_value = "not a json"
43 resp = app._get_results('test url')
44 self.assertEqual(resp, [])
45 self.assertLogged("Error", "when hitting", "test url", "ValueError")
46
35 def test_surfacing(self):47 def test_surfacing(self):
36 app = reddit.App(CONFIG)48 app = reddit.App(CONFIG)
49 results_data = json.loads(get_fixture('reddit-front.json'))['data']['children']
37 with patch.object(app, '_get_available_departments') as mockdep:50 with patch.object(app, '_get_available_departments') as mockdep:
38 with patch.object(app, '_get_results') as mock:51 with patch.object(app, '_get_results') as mock:
39 mock.return_value = json.loads(52 mock.return_value = results_data
40 get_fixture('reddit-front.json'))
41 mockdep.return_value = {}53 mockdep.return_value = {}
42 response = list(app.search(query='',54 response = list(app.search(query='',
43 platform='desktop',55 platform='desktop',
@@ -139,14 +151,14 @@
139 'attributes': [151 'attributes': [
140 {'style': 'highlighted', 'value': '171'},152 {'style': 'highlighted', 'value': '171'},
141 {'style': 'default', 'value': '242 comments'}]153 {'style': 'default', 'value': '242 comments'}]
142 }154 }
143 self.assertEqual(response, should)155 self.assertEqual(response, should)
144156
145 def test_available_departments(self):157 def test_available_departments(self):
146 app = reddit.App(CONFIG)158 app = reddit.App(CONFIG)
159 results_data = json.loads(get_fixture('reddit-departments.json'))['data']['children']
147 with patch.object(app, '_get_results') as mock:160 with patch.object(app, '_get_results') as mock:
148 mock.return_value = json.loads(161 mock.return_value = results_data
149 get_fixture('reddit-departments.json'))
150 response = app._get_available_departments(None, 'EN')162 response = app._get_available_departments(None, 'EN')
151163
152 should = {'canned_query': 'scope://com.canonical.scopes.reddit',164 should = {'canned_query': 'scope://com.canonical.scopes.reddit',
@@ -169,9 +181,9 @@
169181
170 def test_available_departments_with_new_dep(self):182 def test_available_departments_with_new_dep(self):
171 app = reddit.App(CONFIG)183 app = reddit.App(CONFIG)
184 results_data = json.loads(get_fixture('reddit-departments.json'))['data']['children']
172 with patch.object(app, '_get_results') as mock:185 with patch.object(app, '_get_results') as mock:
173 mock.return_value = json.loads(186 mock.return_value = results_data
174 get_fixture('reddit-departments.json'))
175 response = app._get_available_departments('halflife3', 'EN')187 response = app._get_available_departments('halflife3', 'EN')
176188
177 should = {'canned_query': 'scope://com.canonical.scopes.reddit',189 should = {'canned_query': 'scope://com.canonical.scopes.reddit',

Subscribers

People subscribed via source and target branches