Merge lp:~alecu/ubuntuone-client/cleanup-emit-signals into lp:ubuntuone-client

Proposed by Alejandro J. Cura
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 1125
Merged at revision: 1126
Proposed branch: lp:~alecu/ubuntuone-client/cleanup-emit-signals
Merge into: lp:ubuntuone-client
Diff against target: 471 lines (+168/-88)
3 files modified
tests/platform/windows/test_ipc.py (+99/-28)
ubuntuone/platform/windows/ipc.py (+18/-20)
ubuntuone/platform/windows/ipc_client.py (+51/-40)
To merge this branch: bzr merge lp:~alecu/ubuntuone-client/cleanup-emit-signals
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Natalia Bidart (community) Approve
Review via email: mp+73733@code.launchpad.net

Commit message

Connect each IPC client to the signals it should listen to. (LP: #839060)

Description of the change

Connect each IPC client to the signals it should listen to. (LP: #839060)

To post a comment you must log in.
Revision history for this message
Manuel de la Peña (mandel) wrote :

A small comment, sending the entire collection of remote_calls to be signals seems wrong to me. Because remove_calls can be all the calls that can be remotly called from the client. I know that at this point we just have signals.. but who knows, what about using the @signal decorator to create a signals collection? Or at least a subset of all the remote_calls.

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

Looks good to me!

review: Approve
1124. By Alejandro J. Cura

separate the list of signal handlers from the list of remote calls.

1125. By Alejandro J. Cura

fix some tests

Revision history for this message
Manuel de la Peña (mandel) wrote :

Nice!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'tests/platform/windows/test_ipc.py'
--- tests/platform/windows/test_ipc.py 2011-08-26 00:03:30 +0000
+++ tests/platform/windows/test_ipc.py 2011-09-02 15:03:18 +0000
@@ -43,6 +43,7 @@
43 IPCInterface,43 IPCInterface,
44 IPCRoot,44 IPCRoot,
45 PublicFiles,45 PublicFiles,
46 RemoteMeta,
46 Shares,47 Shares,
47 SignalBroadcaster,48 SignalBroadcaster,
48 Status,49 Status,
@@ -58,9 +59,10 @@
58 FoldersClient,59 FoldersClient,
59 FileSystemClient,60 FileSystemClient,
60 PublicFilesClient,61 PublicFilesClient,
62 RemoteClient,
61 StatusClient,63 StatusClient,
62 SyncDaemonClient,64 SyncDaemonClient,
63 SharesClient65 SharesClient,
64)66)
6567
66TEST_PORT = 4040468TEST_PORT = 40404
@@ -272,6 +274,7 @@
272 """Return the public files remote object."""274 """Return the public files remote object."""
273 return self._public_files275 return self._public_files
274276
277
275class TestSignalBroadcaster(MockerTestCase):278class TestSignalBroadcaster(MockerTestCase):
276 """Test the signal broadcaster code."""279 """Test the signal broadcaster code."""
277280
@@ -279,13 +282,15 @@
279 super(TestSignalBroadcaster, self).setUp()282 super(TestSignalBroadcaster, self).setUp()
280 self.client = self.mocker.mock()283 self.client = self.mocker.mock()
281 self.broad_caster = SignalBroadcaster()284 self.broad_caster = SignalBroadcaster()
282 self.broad_caster.clients.append(self.client)
283285
284 def test_remote_register_to_signals(self):286 def test_remote_register_to_signals(self):
285 """Assert that the client was added."""287 """Assert that the client was added."""
286 self.mocker.replay()288 self.mocker.replay()
287 self.broad_caster.remote_register_to_signals(self.client)289 signals = ["demo_signal1", "demo_signal2"]
288 self.assertTrue(self.client in self.broad_caster.clients)290 self.broad_caster.remote_register_to_signals(self.client, signals)
291 for signal in signals:
292 clients = self.broad_caster.clients_per_signal[signal]
293 self.assertTrue(self.client in clients)
289294
290 def test_emit_signal(self):295 def test_emit_signal(self):
291 """Assert that the client method was called."""296 """Assert that the client method was called."""
@@ -299,6 +304,8 @@
299 deferred.addErrback(ANY, ANY, ANY)304 deferred.addErrback(ANY, ANY, ANY)
300 deferred.addErrback(ANY, ANY, ANY)305 deferred.addErrback(ANY, ANY, ANY)
301 self.mocker.replay()306 self.mocker.replay()
307 signals = [signal_name]
308 self.broad_caster.remote_register_to_signals(self.client, signals)
302 self.broad_caster.emit_signal(signal_name, first, second, word=word)309 self.broad_caster.emit_signal(signal_name, first, second, word=word)
303310
304 def test_emit_signal_dead_reference(self):311 def test_emit_signal_dead_reference(self):
@@ -310,10 +317,11 @@
310 self.mocker.replay()317 self.mocker.replay()
311318
312 sb = SignalBroadcaster()319 sb = SignalBroadcaster()
313 sb.remote_register_to_signals(fake_remote_client)320 sb.remote_register_to_signals(fake_remote_client, [sample_signal])
314 self.assertIn(fake_remote_client, sb.clients)321 self.assertIn(fake_remote_client, sb.clients_per_signal[sample_signal])
315 sb.emit_signal(sample_signal)322 sb.emit_signal(sample_signal)
316 self.assertNotIn(fake_remote_client, sb.clients)323 self.assertNotIn(fake_remote_client,
324 sb.clients_per_signal[sample_signal])
317325
318 def test_emit_signal_some_dead_some_not(self):326 def test_emit_signal_some_dead_some_not(self):
319 """Test a clean reference after a dead one."""327 """Test a clean reference after a dead one."""
@@ -328,8 +336,8 @@
328 self.mocker.replay()336 self.mocker.replay()
329337
330 sb = SignalBroadcaster()338 sb = SignalBroadcaster()
331 sb.remote_register_to_signals(fake_dead_remote)339 sb.remote_register_to_signals(fake_dead_remote, [sample_signal])
332 sb.remote_register_to_signals(fake_alive_remote)340 sb.remote_register_to_signals(fake_alive_remote, [sample_signal])
333 sb.emit_signal(sample_signal)341 sb.emit_signal(sample_signal)
334342
335343
@@ -360,8 +368,10 @@
360 fake_remote_client = FakeRemoteClient()368 fake_remote_client = FakeRemoteClient()
361369
362 sb = SignalBroadcaster()370 sb = SignalBroadcaster()
363 sb.remote_register_to_signals(fake_remote_client)371 signals = [fake_remote_client.missing_signal]
364 self.assertIn(fake_remote_client, sb.clients)372 sb.remote_register_to_signals(fake_remote_client, signals)
373 sb_clients = sb.clients_per_signal[fake_remote_client.missing_signal]
374 self.assertIn(fake_remote_client, sb_clients)
365 sb.emit_signal(fake_remote_client.missing_signal)375 sb.emit_signal(fake_remote_client.missing_signal)
366376
367 expected = (377 expected = (
@@ -379,8 +389,10 @@
379 fake_remote_client = FakeRemoteClient()389 fake_remote_client = FakeRemoteClient()
380390
381 sb = SignalBroadcaster()391 sb = SignalBroadcaster()
382 sb.remote_register_to_signals(fake_remote_client)392 signals = [fake_remote_client.failing_signal]
383 self.assertIn(fake_remote_client, sb.clients)393 sb.remote_register_to_signals(fake_remote_client, signals)
394 sb_clients = sb.clients_per_signal[fake_remote_client.failing_signal]
395 self.assertIn(fake_remote_client, sb_clients)
384 sb.emit_signal(fake_remote_client.failing_signal)396 sb.emit_signal(fake_remote_client.failing_signal)
385397
386 expected = (398 expected = (
@@ -392,11 +404,38 @@
392 self.assertIn(expected, warnings)404 self.assertIn(expected, warnings)
393405
394406
407class FakeRemoteObject(object):
408 """A test helper."""
409
410 def __init__(self):
411 """Initialize this test helper."""
412 self.called = []
413
414 def callRemote(self, *args):
415 """A remote call to this object."""
416 self.called.append(args)
417
418
419class RemoteClientTestCase(TestCase):
420 """Tests for the RemoteClient class."""
421
422 def test_register_to_signals(self):
423 """Test the register_to_signals method."""
424 fake_remote_object = FakeRemoteObject()
425 client = RemoteClient(fake_remote_object)
426 client.signal_handlers = ["on_abc"]
427 client.register_to_signals()
428 expected = [
429 ("register_to_signals", client, client.signal_handlers)
430 ]
431 self.assertEqual(fake_remote_object.called, expected)
432
433
395class TestStatusEmitSignals(PerspectiveBrokerTestCase, MockerTestCase):434class TestStatusEmitSignals(PerspectiveBrokerTestCase, MockerTestCase):
396 """Tests that the emit methods are correct."""435 """Tests that the emit methods are correct."""
397436
398 def setUp(self):437 def setUp(self):
399 """Setup tests.""" 438 """Setup tests."""
400 super(TestStatusEmitSignals, self).setUp()439 super(TestStatusEmitSignals, self).setUp()
401 self.signal_method = self.mocker.mock()440 self.signal_method = self.mocker.mock()
402 self.status.syncdaemon_status = self.mocker.mock()441 self.status.syncdaemon_status = self.mocker.mock()
@@ -885,21 +924,11 @@
885 root = yield self.client_factory.getRootObject()924 root = yield self.client_factory.getRootObject()
886 remote = yield root.callRemote('get_status')925 remote = yield root.callRemote('get_status')
887 client = StatusClient(remote)926 client = StatusClient(remote)
927 # set the cb
928 callback_names = [signal + "_cb" for signal in client.signal_handlers]
888 yield client.register_to_signals()929 yield client.register_to_signals()
889 # set the cb930 for callback_name in callback_names:
890 for signal in ['on_content_queue_changed_cb',931 setattr(client, callback_name, self.mocker.mock())
891 'on_invalid_name_cb',
892 'on_broken_node_cb',
893 'on_status_changed_cb',
894 'on_download_started_cb',
895 'on_download_file_progress_cb',
896 'on_download_finished_cb',
897 'on_upload_started_cb',
898 'on_upload_file_progress_cb',
899 'on_upload_finished_cb',
900 'on_account_changed_cb',
901 'on_metaqueue_changed_cb']:
902 setattr(client, signal, self.mocker.mock())
903 defer.returnValue(client)932 defer.returnValue(client)
904933
905 def test_current_status(self):934 def test_current_status(self):
@@ -3119,6 +3148,48 @@
3119 self.sender.handle_default(event_name, **kwargs)3148 self.sender.handle_default(event_name, **kwargs)
31203149
31213150
3151class RemoteMetaTestCase(TestCase):
3152 """Tests for the RemoteMeta metaclass."""
3153
3154 def test_remote_calls_renamed(self):
3155 """The remote_calls are renamed."""
3156 test_token = object()
3157
3158 class TestClass(object):
3159 """A class for testing."""
3160
3161 __metaclass__ = RemoteMeta
3162
3163 remote_calls = ['test_method']
3164
3165 def test_method(self):
3166 """Fake call."""
3167 return test_token
3168
3169 tc = TestClass()
3170 self.assertEquals(tc.test_method(), test_token)
3171 self.assertEquals(tc.remote_test_method(), test_token)
3172
3173 def test_signal_handlers_renamed(self):
3174 """The signal_handlers are renamed."""
3175 test_token = object()
3176
3177 class TestClass(object):
3178 """A class for testing."""
3179
3180 __metaclass__ = RemoteMeta
3181
3182 signal_handlers = ['test_signal_handler']
3183
3184 def test_signal_handler(self):
3185 """Fake call."""
3186 return test_token
3187
3188 tc = TestClass()
3189 self.assertEquals(tc.test_signal_handler(), test_token)
3190 self.assertEquals(tc.remote_test_signal_handler(), test_token)
3191
3192
3122class TestIPCRoot(BaseIPCTestCase):3193class TestIPCRoot(BaseIPCTestCase):
3123 """Ensure that the IPCRoot works as expected."""3194 """Ensure that the IPCRoot works as expected."""
31243195
31253196
=== modified file 'ubuntuone/platform/windows/ipc.py'
--- ubuntuone/platform/windows/ipc.py 2011-08-26 00:03:30 +0000
+++ ubuntuone/platform/windows/ipc.py 2011-09-02 15:03:18 +0000
@@ -20,6 +20,7 @@
2020
21import logging21import logging
2222
23from collections import defaultdict
23from threading import Thread24from threading import Thread
2425
25from twisted.internet import defer, reactor26from twisted.internet import defer, reactor
@@ -139,10 +140,10 @@
139 """140 """
140141
141 def __new__(cls, name, bases, attrs):142 def __new__(cls, name, bases, attrs):
142 remote_calls = attrs.get('remote_calls', None)143 remote_calls = attrs.get('remote_calls', [])
143 if remote_calls:144 signal_handlers = attrs.get('signal_handlers', [])
144 for current in remote_calls:145 for current in remote_calls + signal_handlers:
145 attrs['remote_' + current] = attrs[current]146 attrs['remote_' + current] = attrs[current]
146 return super(RemoteMeta, cls).__new__(cls, name, bases, attrs)147 return super(RemoteMeta, cls).__new__(cls, name, bases, attrs)
147148
148149
@@ -154,7 +155,7 @@
154155
155 def __init__(self):156 def __init__(self):
156 """Create a new instance."""157 """Create a new instance."""
157 self.clients = []158 self.clients_per_signal = defaultdict(set)
158159
159 def _ignore_no_such_method(self, failure, signal_name, current_client):160 def _ignore_no_such_method(self, failure, signal_name, current_client):
160 """NoSuchMethod is not an error, ignore it."""161 """NoSuchMethod is not an error, ignore it."""
@@ -167,33 +168,29 @@
167 current_client, failure.value)168 current_client, failure.value)
168 logger.warning('Traceback is:\n%s', failure.printDetailedTraceback())169 logger.warning('Traceback is:\n%s', failure.printDetailedTraceback())
169170
170 def remote_register_to_signals(self, client):171 def remote_register_to_signals(self, client, signals):
171 """Allow a client to register to a signal."""172 """Allow a client to register to some signals."""
172 if client not in self.clients:173 for signal in signals:
173 self.clients.append(client)174 self.clients_per_signal[signal].add(client)
174 else:
175 logger.warning('Client %s tried to register twice.', client)
176175
177 def remote_unregister_to_signals(self, client):176 def remote_unregister_to_signals(self, client):
178 """Allow a client to register to a signal."""177 """Allow a client to unregister from the signal."""
179 if client in self.clients:178 for connected_clients in self.clients_per_signal.values():
180 self.clients.remove(client)179 if client in connected_clients:
181 else:180 connected_clients.remove(client)
182 logger.warning('Tried to remove %s when was not registered.',
183 client)
184181
185 def emit_signal(self, signal_name, *args, **kwargs):182 def emit_signal(self, signal_name, *args, **kwargs):
186 """Emit the given signal to the clients."""183 """Emit the given signal to the clients."""
187 logger.debug("emitting %r to all connected clients.", signal_name)184 logger.debug("emitting %r to all connected clients.", signal_name)
188 dead_clients = []185 dead_clients = set()
189 for current_client in self.clients:186 for current_client in self.clients_per_signal[signal_name]:
190 try:187 try:
191 d = current_client.callRemote(signal_name, *args, **kwargs)188 d = current_client.callRemote(signal_name, *args, **kwargs)
192 d.addErrback(self._ignore_no_such_method, signal_name,189 d.addErrback(self._ignore_no_such_method, signal_name,
193 current_client)190 current_client)
194 d.addErrback(self._other_failure, signal_name, current_client)191 d.addErrback(self._other_failure, signal_name, current_client)
195 except DeadReferenceError:192 except DeadReferenceError:
196 dead_clients.append(current_client)193 dead_clients.add(current_client)
197 for client in dead_clients:194 for client in dead_clients:
198 self.remote_unregister_to_signals(client)195 self.remote_unregister_to_signals(client)
199196
@@ -379,6 +376,7 @@
379376
380 def __init__(self, root, main, volume_manager, action_queue):377 def __init__(self, root, main, volume_manager, action_queue):
381 """ Creates the instance."""378 """ Creates the instance."""
379 super(SyncDaemon, self).__init__()
382 self.service = SyncdaemonService(root, main, volume_manager,380 self.service = SyncdaemonService(root, main, volume_manager,
383 action_queue)381 action_queue)
384 self.clients = []382 self.clients = []
385383
=== modified file 'ubuntuone/platform/windows/ipc_client.py'
--- ubuntuone/platform/windows/ipc_client.py 2011-07-15 17:03:13 +0000
+++ ubuntuone/platform/windows/ipc_client.py 2011-09-02 15:03:18 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Author: Manuel de la Pena<manuel@canonical.com>3# Authors: Manuel de la Pena <manuel@canonical.com>
4# Alejandro J. Cura <alecu@canonical.com>
4#5#
5# Copyright 2011 Canonical Ltd.6# Copyright 2011 Canonical Ltd.
6#7#
@@ -65,13 +66,16 @@
65class RemoteClient(object):66class RemoteClient(object):
66 """Represent a client for remote calls."""67 """Represent a client for remote calls."""
6768
69 signal_handlers = []
70
68 def __init__(self, remote_object):71 def __init__(self, remote_object):
69 """Create instance."""72 """Create instance."""
70 self.remote = remote_object73 self.remote = remote_object
7174
72 def register_to_signals(self):75 def register_to_signals(self):
73 """Register to the signals."""76 """Register to the signals."""
74 return self.remote.callRemote('register_to_signals', self)77 return self.remote.callRemote('register_to_signals', self,
78 self.signal_handlers)
7579
76 def unregister_to_signals(self):80 def unregister_to_signals(self):
77 """Register to the signals."""81 """Register to the signals."""
@@ -124,18 +128,20 @@
124 __metaclass__ = RemoteMeta128 __metaclass__ = RemoteMeta
125129
126 # calls that will be accessible remotely130 # calls that will be accessible remotely
127 remote_calls = ['on_content_queue_changed',131 signal_handlers = [
128 'on_invalid_name',132 'on_content_queue_changed',
129 'on_broken_node',133 'on_invalid_name',
130 'on_status_changed',134 'on_broken_node',
131 'on_download_started',135 'on_status_changed',
132 'on_download_file_progress',136 'on_download_started',
133 'on_download_finished',137 'on_download_file_progress',
134 'on_upload_started',138 'on_download_finished',
135 'on_upload_file_progress',139 'on_upload_started',
136 'on_upload_finished',140 'on_upload_file_progress',
137 'on_account_changed',141 'on_upload_finished',
138 'on_metaqueue_changed']142 'on_account_changed',
143 'on_metaqueue_changed',
144 ]
139145
140 def __init__(self, remote_status):146 def __init__(self, remote_status):
141 """Creates the instance."""147 """Creates the instance."""
@@ -228,7 +234,7 @@
228 __metaclass__ = RemoteMeta234 __metaclass__ = RemoteMeta
229235
230 # calls that will be accessible remotely236 # calls that will be accessible remotely
231 remote_calls = ['on_event',]237 signal_handlers = ['on_event',]
232238
233 def __init__(self, remote_events):239 def __init__(self, remote_events):
234 """Creates the instance."""240 """Creates the instance."""
@@ -249,8 +255,7 @@
249 __metaclass__ = RemoteMeta255 __metaclass__ = RemoteMeta
250256
251 # calls that will be accessible remotely257 # calls that will be accessible remotely
252 remote_calls = ['on_root_mismatch',258 signal_handlers = ['on_root_mismatch', 'on_quota_exceeded']
253 'on_quota_exceeded']
254259
255 def __init__(self, remote_daemon):260 def __init__(self, remote_daemon):
256 """Creates the instance."""261 """Creates the instance."""
@@ -332,17 +337,19 @@
332 __metaclass__ = RemoteMeta337 __metaclass__ = RemoteMeta
333338
334 # calls that will be accessible remotely339 # calls that will be accessible remotely
335 remote_calls = ['on_share_deleted',340 signal_handlers = [
336 'on_share_changed',341 'on_share_deleted',
337 'on_share_delete_error',342 'on_share_changed',
338 'on_share_created',343 'on_share_delete_error',
339 'on_share_create_error',344 'on_share_created',
340 'on_share_answer_response',345 'on_share_create_error',
341 'on_new_share',346 'on_share_answer_response',
342 'on_share_subscribed',347 'on_new_share',
343 'on_share_subscribe_error',348 'on_share_subscribed',
344 'on_share_unsubscribed',349 'on_share_subscribe_error',
345 'on_share_unsubscribe_error']350 'on_share_unsubscribed',
351 'on_share_unsubscribe_error',
352 ]
346353
347 def __init__(self, remote_shares):354 def __init__(self, remote_shares):
348 """Create the instance."""355 """Create the instance."""
@@ -556,14 +563,16 @@
556 __metaclass__ = RemoteMeta563 __metaclass__ = RemoteMeta
557564
558 # calls that will be accessible remotely565 # calls that will be accessible remotely
559 remote_calls = ['on_folder_created',566 signal_handlers = [
560 'on_folder_create_error',567 'on_folder_created',
561 'on_folder_deleted',568 'on_folder_create_error',
562 'on_folder_delete_error',569 'on_folder_deleted',
563 'on_folder_subscribed',570 'on_folder_delete_error',
564 'on_folder_subscribe_error',571 'on_folder_subscribed',
565 'on_folder_unsubscribed',572 'on_folder_subscribe_error',
566 'on_folder_unsubscribe_error']573 'on_folder_unsubscribed',
574 'on_folder_unsubscribe_error',
575 ]
567576
568 def __init__(self, remote_folders):577 def __init__(self, remote_folders):
569 """Creates the instance."""578 """Creates the instance."""
@@ -636,10 +645,12 @@
636 __metaclass__ = RemoteMeta645 __metaclass__ = RemoteMeta
637646
638 # calls that will be accessible remotely647 # calls that will be accessible remotely
639 remote_calls = ['on_public_access_changed',648 signal_handlers = [
640 'on_public_access_change_error',649 'on_public_access_changed',
641 'on_public_files_list',650 'on_public_access_change_error',
642 'on_public_files_list_error']651 'on_public_files_list',
652 'on_public_files_list_error',
653 ]
643654
644 def __init__(self, remote_public_files):655 def __init__(self, remote_public_files):
645 super(PublicFilesClient, self).__init__(remote_public_files)656 super(PublicFilesClient, self).__init__(remote_public_files)

Subscribers

People subscribed via source and target branches