Merge lp:~dobey/ubuntuone-client/no-httplib2 into lp:ubuntuone-client

Proposed by dobey
Status: Merged
Approved by: Natalia Bidart
Approved revision: 424
Merged at revision: not available
Proposed branch: lp:~dobey/ubuntuone-client/no-httplib2
Merge into: lp:ubuntuone-client
Diff against target: 396 lines (+102/-107)
3 files modified
bin/ubuntuone-preferences (+80/-90)
contrib/mocker.py (+4/-1)
tests/test_preferences.py (+18/-16)
To merge this branch: bzr merge lp:~dobey/ubuntuone-client/no-httplib2
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
John Lenton (community) Approve
Review via email: mp+21167@code.launchpad.net

Commit message

Unify the 2 rest request methods to do async requests
Drops the httplib2 dependency
Also updated the tests to fix #537443

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

There is a pyflakes issue:

== Python Lint Notices ==

./tests/test_preferences.py:
    24: 'KWARGS' imported but unused

make: *** [lint] Error 1

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :
Download full text (10.6 KiB)

> There is a pyflakes issue:
>
> == Python Lint Notices ==
>
> ./tests/test_preferences.py:
> 24: 'KWARGS' imported but unused
>
> make: *** [lint] Error 1

I'm also getting:

nessita@dali:~/canonical/ubuntuone-client/review_no-httplib2$ ./contrib/test tests/test_preferences.py
contrib.testing.testcase
  DBusTwistedTestCase
    runTest ... [OK]
tests.test_preferences
  PreferencesTests
    test_bw_throttling ... Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 576, in msg_reply_handler
    reply_handler(*message.get_args_list(**get_args_opts))
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 391, in _introspect_reply_handler
    self._introspect_execute_queue()
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 378, in _introspect_execute_queue
    proxy_method(*args, **keywords)
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 132, in __call__
    **keywords)
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 585, in call_async
    require_main_loop=require_main_loop)
dbus.exceptions.DBusException: Connection is disconnected - unable to make method call
                                                [OK]
    test_list_devices_fills_devices_list_with_fake_result_when_empty ... Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 576, in msg_reply_handler
    reply_handler(*message.get_args_list(**get_args_opts))
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 391, in _introspect_reply_handler
    self._introspect_execute_queue()
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 378, in _introspect_execute_queue
    proxy_method(*args, **keywords)
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 132, in __call__
    **keywords)
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 585, in call_async
    require_main_loop=require_main_loop)
dbus.exceptions.DBusException: Connection is disconnected - unable to make method call
  [OK]
    test_list_devices_shows_devices_list ... Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 576, in msg_reply_handler
    reply_handler(*message.get_args_list(**get_args_opts))
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 391, in _introspect_reply_handler
    self._introspect_execute_queue()
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 378, in _introspect_execute_queue
    proxy_method(*args, **keywords)
  File "/usr/lib/pymodules/python2.6/dbus/proxies.py", line 132, in __call__
    **keywords)
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 585, in call_async
    require_main_loop=require_main_loop)
dbus.exceptions.DBusException: Connection is disconnected - unable to make method call
                              [OK]
    test_list_devices_shows_real_devices_list ... Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.6/dbus/connection.py", line 576, in msg_reply_handler
    reply_handler(*message.get_args_list(**get_args_opts))
  File "/usr/lib/pymodules/pyt...

423. By dobey

Fix lint issue

Revision history for this message
dobey (dobey) wrote :

The lint is fixed.

The DBusExceptions seem to be a result of how contrib/test is called. Perhaps a bug in that script, but I am not sure...

./contrib/test tests/test_preferences.py gives those exceptions

./contrib/test -t tests.test_preferences however, does not

Revision history for this message
John Lenton (chipaca) wrote :

I'm getting a lot of very strange, apparently unrelated test failures with this branch (and not with trunk). It's got something to do with some kind of resource not going away, because I can run each of the test modules individually, or all the tests except test_preferences, they pass; if I run test_preferences and one other one, I get these failures.

They look like this:
    test_udf_deleted ... Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/twisted/trial/unittest.py", line 951, in _cleanUp
    clean = util._Janitor(self, result).postCaseCleanup()
  File "/usr/lib/python2.6/dist-packages/twisted/trial/util.py", line 131, in postCaseCleanup
    calls = self._cleanPending()
  File "/usr/lib/python2.6/dist-packages/twisted/internet/utils.py", line 211, in warningSuppressingWrapper
    return runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw)
  File "/usr/lib/python2.6/dist-packages/twisted/internet/utils.py", line 191, in runWithWarningsSuppressed
    result = f(*a, **kw)
  File "/usr/lib/python2.6/dist-packages/twisted/trial/util.py", line 172, in _cleanPending
    reactor.iterate(0)
  File "/usr/lib/python2.6/dist-packages/twisted/trial/unittest.py", line 981, in _
    stacklevel=2, category=DeprecationWarning)
exceptions.TypeError: __nonzero__ should return bool or int, returned Mock
[ERROR]

review: Needs Fixing
424. By dobey

Upstream patch to mocker.py to fix the __nonzero__ test failures

Revision history for this message
dobey (dobey) wrote :

The weird __nonzero__ tracebacks are fixed now.

Revision history for this message
John Lenton (chipaca) wrote :

On Mon, Mar 15, 2010 at 08:48:31PM -0000, Rodney Dawes wrote:
> The weird __nonzero__ tracebacks are fixed now.

yes, they are. Good to go!

 review approve

review: Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

> The weird __nonzero__ tracebacks are fixed now.

Approving now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/ubuntuone-preferences'
2--- bin/ubuntuone-preferences 2010-03-11 19:10:01 +0000
3+++ bin/ubuntuone-preferences 2010-03-15 20:46:20 +0000
4@@ -28,7 +28,6 @@
5 import os
6 import gettext
7 import gnomekeyring
8-import httplib2
9 import simplejson
10 from oauth import oauth
11 from ubuntuone import clientdefs
12@@ -82,6 +81,50 @@
13 secret = items[0].secret
14 return oauth.OAuthToken.from_string(secret)
15
16+def rest_response_handler(source, condition, conn, callback):
17+ """
18+ Helper that handles the REST response.
19+ """
20+ result = None
21+ response = conn.getresponse() # shouldn't block
22+ if response.status == 200:
23+ data = response.read() # neither should this
24+ result = simplejson.loads(data)
25+ else:
26+ result = response.reason
27+ callback(result)
28+ return False
29+
30+def make_rest_request(url=None, method='GET',
31+ callback=None, keyring=None):
32+ """
33+ Helper that makes an oauth-wrapped REST request.
34+ """
35+ token = get_access_token(keyring)
36+ consumer = oauth.OAuthConsumer('ubuntuone', 'hammertime')
37+
38+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(
39+ http_url=url,
40+ http_method=method,
41+ oauth_consumer=consumer,
42+ token=token,
43+ parameters='')
44+ oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
45+ consumer, token)
46+
47+ scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
48+
49+ conn = httplib.HTTPSConnection(netloc)
50+ try:
51+ conn.request(method, path, headers=oauth_request.to_header())
52+ except socket.error:
53+ callback(None)
54+ else:
55+ glib.io_add_watch(
56+ conn.sock,
57+ glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP,
58+ rest_response_handler, conn, callback)
59+
60
61 class DevicesWidget(gtk.Table):
62 """
63@@ -224,34 +267,6 @@
64 self.status_label.set_markup("<b>Error:</b> %s" % msg)
65 logger.error(msg)
66
67- def request(self, path='', method='GET'):
68- """
69- Helper that makes an oauth-wrapped rest request.
70-
71- XXX duplication with request_REST_info (but this one should be async).
72- """
73- url = self.base_url + path
74-
75- token = get_access_token(self.keyring)
76-
77- oauth_request = oauth.OAuthRequest.from_consumer_and_token(
78- http_url=url,
79- http_method=method,
80- oauth_consumer=self.consumer,
81- token=token,
82- parameters='')
83- oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
84- self.consumer, token)
85-
86- scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
87-
88- conn = httplib.HTTPSConnection(netloc)
89- try:
90- conn.request(method, path, headers=oauth_request.to_header())
91- except socket.error:
92- return None
93- return conn
94-
95 def get_devices(self):
96 """
97 Ask the server for a list of devices
98@@ -264,31 +279,19 @@
99 self.error("No token in the keyring")
100 self.devices = []
101 else:
102- self.consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
103-
104- self.conn = self.request()
105- if self.conn is None:
106- self.clear_devices_view()
107- self.error('Unable to connect')
108- else:
109- glib.io_add_watch(
110- self.conn.sock,
111- glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP,
112- self.parse_devices)
113-
114- def parse_devices(self, *a):
115+ make_rest_request(url=self.base_url, keyring=self.keyring,
116+ callback=self.parse_devices)
117+
118+ def parse_devices(self, result):
119 """
120 Parse the list of devices, and hook up list_devices if it worked.
121 """
122- response = self.conn.getresponse() # shouldn't block
123- if response.status == 200:
124- response = response.read() # neither should this
125- self.devices = simplejson.loads(response)
126+ if result:
127+ self.devices = result
128 gobject.idle_add(self.list_devices)
129 else:
130 self.clear_devices_view()
131- self.error(response.reason)
132- return False
133+ self.error(result)
134
135 def clear_devices_view(self):
136 """
137@@ -414,16 +417,10 @@
138
139 Starts an async request to remove a device.
140 """
141- self.conn = self.request('remove/%s/%s' % (kind.lower(), token))
142- if self.conn is None:
143- self.clear_devices_view()
144- self.error('Unable to connect')
145- else:
146- glib.io_add_watch(
147- self.conn.sock,
148- glib.IO_IN | glib.IO_PRI | glib.IO_ERR | glib.IO_HUP,
149- self.parse_devices)
150-
151+ make_rest_request(url=('%sremove/%s/%s' % (self.base_url,
152+ kind.lower(), token)),
153+ keyring=self.keyring,
154+ callback=self.parse_devices)
155
156
157 class UbuntuOneDialog(gtk.Dialog):
158@@ -479,11 +476,11 @@
159
160 def __dbus_error(self, error):
161 """Error getting throttling config."""
162- print repr(error)
163+ logger.error(error)
164
165 def __sd_error(self, error):
166 """Error talking to syncdaemon."""
167- print repr(error)
168+ logger.error(error)
169
170 def __got_state(self, state):
171 """Got the state of syncdaemon."""
172@@ -525,42 +522,30 @@
173 'percent' : percent })
174 self.usage_graph.set_fraction(percent / 100)
175
176- def request_REST_info(self, url, method):
177- """Make a REST request and return the resulting dict, or None."""
178- consumer = oauth.OAuthConsumer("ubuntuone", "hammertime")
179- token = get_access_token(self.keyring)
180- request = oauth.OAuthRequest.from_consumer_and_token(
181- http_url=url, http_method=method, oauth_consumer=consumer,
182- token=token)
183- request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
184- consumer, token)
185- client = httplib2.Http()
186- headers = {}
187- headers.update(request.to_header())
188- resp, content = client.request(url, method, headers=headers)
189- if resp['status'] == '200':
190- return simplejson.loads(content)
191- # FIXME: Log json parsing failures
192- else:
193- # FIXME: Log errors
194- return None
195+ def got_quota_info(self, quota):
196+ """Handle the result from the quota REST call."""
197+ if quota:
198+ self.update_quota_display(quota['used'], quota['total'])
199
200 def request_quota_info(self):
201 """Request new quota info from server, and update display."""
202- quota = self.request_REST_info('https://one.ubuntu.com/api/quota/',
203- 'GET')
204- if quota:
205- self.update_quota_display(quota['used'], quota['total'])
206+ make_rest_request(url='https://one.ubuntu.com/api/quota/',
207+ keyring=self.keyring,
208+ callback=self.got_quota_info)
209
210- def request_account_info(self):
211- """Request account info from server, and update display."""
212- user = self.request_REST_info('https://one.ubuntu.com/api/account/',
213- 'GET')
214+ def got_account_info(self, user):
215+ """Handle the result from the account REST call."""
216 if user:
217 self.name_label.set_text(user['nickname'])
218 self.user_label.set_text(user['username'])
219 self.mail_label.set_text(user['email'])
220
221+ def request_account_info(self):
222+ """Request account info from server, and update display."""
223+ make_rest_request(url='https://one.ubuntu.com/api/account/',
224+ keyring=self.keyring,
225+ callback=self.got_account_info)
226+
227 def toggle_db_sync(self, dbname, disable=False):
228 """
229 Toggle whether a db in desktopcouch is synchronized to the
230@@ -683,7 +668,6 @@
231 self.notebook.append_page(self.devices)
232 self.notebook.set_tab_label_text(self.devices, _("Devices"))
233 self.devices.show_all()
234- self.devices.get_devices()
235
236 # Services tab
237 services = gtk.VBox(spacing=12)
238@@ -742,12 +726,17 @@
239 """Show our dialog, since we can do stuff now."""
240 self.disconnect_signal_handlers()
241 dialog = UbuntuOneDialog()
242+ dialog.show()
243 dialog.request_quota_info()
244 dialog.request_account_info()
245- dialog.show()
246+ dialog.devices.get_devices()
247
248 def got_oautherror(self, message=None):
249 """Got an error during oauth."""
250+ if message:
251+ logger.error(message)
252+ else:
253+ logger.error(_("OAuthError with no message."))
254 gtk.main_quit()
255
256 def got_authdenied(self):
257@@ -756,6 +745,7 @@
258
259 def got_dbus_error(self, error):
260 """Got a DBusError."""
261+ logger.error(error)
262 gtk.main_quit()
263
264 def register_signal_handlers(self):
265@@ -804,7 +794,7 @@
266 gettext.textdomain(clientdefs.GETTEXT_PACKAGE)
267
268 gtk.rc_parse_string(RCSTYLE)
269- gobject.set_application_name("Ubuntu One")
270+ gobject.set_application_name("ubuntuone-preferences")
271
272 try:
273 login = UbuntuoneLoginHandler()
274
275=== modified file 'contrib/mocker.py'
276--- contrib/mocker.py 2009-05-12 13:36:05 +0000
277+++ contrib/mocker.py 2010-03-15 20:46:20 +0000
278@@ -1092,9 +1092,12 @@
279
280 def __nonzero__(self):
281 try:
282- return self.__mocker_act__("nonzero")
283+ result = self.__mocker_act__("nonzero")
284 except MatchError, e:
285 return True
286+ if type(result) is Mock:
287+ return True
288+ return result
289
290 def __iter__(self):
291 # XXX On py3k, when next() becomes __next__(), we'll be able
292
293=== modified file 'tests/test_preferences.py'
294--- tests/test_preferences.py 2010-03-08 05:24:51 +0000
295+++ tests/test_preferences.py 2010-03-15 20:46:20 +0000
296@@ -21,7 +21,7 @@
297 import os
298 import gnomekeyring
299
300-from contrib.mocker import MockerTestCase, KWARGS
301+from contrib.mocker import MockerTestCase
302 from contrib.testing.testcase import DBusTwistedTestCase, FakeLogin
303 from twisted.internet import defer
304 from twisted.python.failure import Failure
305@@ -48,7 +48,6 @@
306
307 # For testing keyring queries
308 self.keyring = self.mocker.mock()
309- self.u1prefs.httplib2 = self.mocker.mock()
310 self.item = self.mocker.mock(gnomekeyring.Found)
311
312 self.item_id = 999
313@@ -72,6 +71,8 @@
314 self.mocker.count(0, None)
315 self.mocker.result(None)
316
317+ self.u1prefs.make_rest_request = self.make_rest_request
318+
319 def tearDown(self):
320 # collect all signal receivers registered during the test
321 signal_receivers = set()
322@@ -88,10 +89,19 @@
323 d.addBoth(lambda _: DBusTwistedTestCase.tearDown(self))
324 return d
325
326+ def make_rest_request(self, url=None, method='GET',
327+ callback=None, keyring=None):
328+ """Override the real request call to mock some stuff."""
329+ if callback:
330+ callback(self.content)
331+ else:
332+ raise Exception('No callback provided.')
333+
334 def test_bw_throttling(self):
335 """Test that toggling bw throttling works correctly."""
336 self.mocker.replay()
337 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
338+ widget.update_bw_settings = self.mocker.mock()
339 try:
340 widget.devices = []
341 widget.list_devices()
342@@ -116,6 +126,7 @@
343 def test_list_devices_fills_devices_list_with_fake_result_when_empty(self):
344 self.mocker.replay()
345 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
346+ widget.update_bw_settings = self.mocker.mock()
347 try:
348 widget.devices = []
349 widget.list_devices()
350@@ -129,6 +140,7 @@
351 def test_list_devices_shows_devices_list(self):
352 self.mocker.replay()
353 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
354+ widget.update_bw_settings = self.mocker.mock()
355 try:
356 widget.devices = []
357 widget.list_devices()
358@@ -159,6 +171,7 @@
359 def test_list_devices_shows_real_devices_list(self):
360 self.mocker.replay()
361 widget = self.u1prefs.DevicesWidget(None, keyring=self.keyring)
362+ widget.update_bw_settings = self.mocker.mock()
363 try:
364 widget.devices = [{'kind': 'Computer',
365 'description': 'xyzzy',
366@@ -203,12 +216,7 @@
367
368 def test_request_quota_info(self):
369 """Test that we can request the quota info properly."""
370- response = { 'status' : '200' }
371- content = '{"total":2048, "used":1024}'
372- client = self.mocker.mock()
373- self.expect(self.u1prefs.httplib2.Http()).result(client)
374- self.expect(client.request('https://one.ubuntu.com/api/quota/',
375- 'GET', KWARGS)).result((response, content))
376+ self.content = {"total":2048, "used":1024}
377 self.mocker.replay()
378 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
379 self.assertTrue(dialog is not None)
380@@ -219,14 +227,8 @@
381
382 def test_request_account_info(self):
383 """Test that we can request the account info properly."""
384- response = { 'status' : '200' }
385- content = '''{"username": "ubuntuone", "nickname": "Ubuntu One",
386- "email": "uone@example.com"}
387- '''
388- client = self.mocker.mock()
389- self.expect(self.u1prefs.httplib2.Http()).result(client)
390- self.expect(client.request('https://one.ubuntu.com/api/account/',
391- 'GET', KWARGS)).result((response, content))
392+ self.content = {"username": "ubuntuone", "nickname": "Ubuntu One",
393+ "email": "uone@example.com"}
394 self.mocker.replay()
395 dialog = self.u1prefs.UbuntuOneDialog(keyring=self.keyring)
396 self.assertTrue(dialog is not None)

Subscribers

People subscribed via source and target branches