Merge lp:~facundo/magicicada-gui/show-md into lp:magicicada-gui
- show-md
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Natalia Bidart | ||||
Approved revision: | 44 | ||||
Merged at revision: | 47 | ||||
Proposed branch: | lp:~facundo/magicicada-gui/show-md | ||||
Merge into: | lp:magicicada-gui | ||||
Diff against target: |
348 lines (+123/-24) 4 files modified
magicicada/dbusiface.py (+30/-3) magicicada/syncdaemon.py (+9/-0) magicicada/tests/test_dbusiface.py (+49/-13) magicicada/tests/test_syncdaemon.py (+35/-8) |
||||
To merge this branch: | bzr merge lp:~facundo/magicicada-gui/show-md | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart | Approve | ||
Review via email: mp+27406@code.launchpad.net |
Commit message
Description of the change
Get and deliver metadata for a given path
Natalia Bidart (nataliabidart) wrote : | # |
I was just thinking... what if the "code" for syncdaemon.
I just realized this when implementing the GUI part. Let me know what you think.
Natalia Bidart (nataliabidart) wrote : | # |
2 more things are needed:
(a) Could you please return a python dictionary instead a dbus dictionary? I'd want to keep dbus out the GTK layer.
(b) If the path doesn't exist, we'd need a special value, not a string saying "Not a valid path". This way we can ease the future translations, and we can detect this special case in a proper way.
Facundo Batista (facundo) wrote : | # |
After talking through IRC, what will do is:
- Will use "path" itself as the "code".
- The constant used in dbusiface.py in case of no path will be NOT_SYNCHED_PATH
- Will return a Py dict, instead a str
- 44. By Facundo Batista
-
Some docstring fixes and small changes.
Facundo Batista (facundo) wrote : | # |
I just make it to return the DBus types to the GUI.
Naty does not want it, but I thought duck typing should work... even thinking that, I tried to make tests to check that the types were converted, but...
>>> isinstance(
True
and
>>> isinstance(
True
So, actually, it would be too tough and too fake to assert that, and it should work rightaway.
Preview Diff
1 | === modified file 'magicicada/dbusiface.py' |
2 | --- magicicada/dbusiface.py 2010-06-05 15:57:34 +0000 |
3 | +++ magicicada/dbusiface.py 2010-06-14 18:14:25 +0000 |
4 | @@ -50,11 +50,19 @@ |
5 | "(Move)\(share_id=(.*?), node_id=(.*?), old_parent_id=(.*?), " |
6 | "new_parent_id=(.*?), new_name=(.*?)\)") |
7 | |
8 | +# DBus exceptions store the type inside, as a string :| |
9 | +DBUSERR_NOREPLY = 'org.freedesktop.DBus.Error.NoReply' |
10 | +DBUSERR_NAMENOOWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner' |
11 | +DBUSERR_PYKEYERROR = 'org.freedesktop.DBus.Python.KeyError' |
12 | + |
13 | +# some constants |
14 | +NOT_SYNCHED_PATH = "Not a valid path!" |
15 | + |
16 | |
17 | def _is_retry_exception(err): |
18 | """Check if the exception is a retry one.""" |
19 | if isinstance(err, dbus.exceptions.DBusException): |
20 | - if err.get_dbus_name() == 'org.freedesktop.DBus.Error.NoReply': |
21 | + if err.get_dbus_name() == DBUSERR_NOREPLY: |
22 | return True |
23 | return False |
24 | |
25 | @@ -323,8 +331,7 @@ |
26 | try: |
27 | self._bus.get_name_owner('com.ubuntuone.SyncDaemon') |
28 | except dbus.exceptions.DBusException, err: |
29 | - if err.get_dbus_name() != \ |
30 | - 'org.freedesktop.DBus.Error.NameHasNoOwner': |
31 | + if err.get_dbus_name() != DBUSERR_NAMENOOWNER: |
32 | raise |
33 | started = False |
34 | else: |
35 | @@ -381,3 +388,23 @@ |
36 | d = self.sync_daemon_tool.list_shared() |
37 | d.addCallback(process) |
38 | return d |
39 | + |
40 | + @retryable |
41 | + def get_metadata(self, path): |
42 | + """Return the raw metadata.""" |
43 | + logger.info("Getting metadata for %r", path) |
44 | + |
45 | + def fix_failure(failure): |
46 | + """Get the failure and return a nice message.""" |
47 | + if failure.check(dbus.exceptions.DBusException): |
48 | + if failure.value.get_dbus_name() == DBUSERR_PYKEYERROR: |
49 | + return NOT_SYNCHED_PATH |
50 | + return failure |
51 | + |
52 | + def process(metadata): |
53 | + logger.debug("Got metadata for path %r: %r", path, metadata) |
54 | + return dict(metadata) |
55 | + |
56 | + d = self.sync_daemon_tool.get_metadata(path) |
57 | + d.addCallbacks(process, fix_failure) |
58 | + return d |
59 | |
60 | === modified file 'magicicada/syncdaemon.py' |
61 | --- magicicada/syncdaemon.py 2010-06-11 20:09:11 +0000 |
62 | +++ magicicada/syncdaemon.py 2010-06-14 18:14:25 +0000 |
63 | @@ -104,6 +104,7 @@ |
64 | self.on_folders_changed_callback = NO_OP |
65 | self.on_shares_to_me_changed_callback = NO_OP |
66 | self.on_shares_to_others_changed_callback = NO_OP |
67 | + self.on_metadata_ready_callback = None # mandatory |
68 | |
69 | # mq needs to be polled to know progress |
70 | self._mqcaller = None |
71 | @@ -282,3 +283,11 @@ |
72 | """Tell the SyncDaemon that the user wants it to disconnect.""" |
73 | logger.info("Telling u1.SD to disconnect") |
74 | self.dbus.disconnect() |
75 | + |
76 | + def get_metadata(self, path): |
77 | + """Get the metadata for given path.""" |
78 | + if self.on_metadata_ready_callback is None: |
79 | + raise ValueError("Missing the mandatory cback for get_metadata.") |
80 | + |
81 | + d = self.dbus.get_metadata(path) |
82 | + d.addCallback(lambda resp: self.on_metadata_ready_callback(path, resp)) |
83 | |
84 | === modified file 'magicicada/tests/test_dbusiface.py' |
85 | --- magicicada/tests/test_dbusiface.py 2010-06-03 03:58:02 +0000 |
86 | +++ magicicada/tests/test_dbusiface.py 2010-06-14 18:14:25 +0000 |
87 | @@ -43,7 +43,7 @@ |
88 | del self._callbacks[(dbus_interface, signal_name)] |
89 | |
90 | def get_name_owner(self, name): |
91 | - """Fakes the response of the method.""" |
92 | + """Fake the response of the method.""" |
93 | assert name == 'com.ubuntuone.SyncDaemon' |
94 | if isinstance(self.fake_name_owner, str): |
95 | return self.fake_name_owner |
96 | @@ -69,7 +69,10 @@ |
97 | return defer.Deferred() |
98 | methname, response = self._fake_response |
99 | assert methname == name |
100 | - return response |
101 | + if isinstance(response, Exception): |
102 | + return defer.fail(response) |
103 | + else: |
104 | + return defer.succeed(response) |
105 | return f |
106 | |
107 | |
108 | @@ -104,9 +107,8 @@ |
109 | return called_args |
110 | |
111 | def fake_sdt_response(self, method_name, response): |
112 | - """Fakes SDT answer in deferred mode.""" |
113 | - self.dbus.sync_daemon_tool._fake_response = (method_name, |
114 | - defer.succeed(response)) |
115 | + """Fake SDT answer in deferred mode.""" |
116 | + self.dbus.sync_daemon_tool._fake_response = (method_name, response) |
117 | |
118 | class TestSignalHooking(SafeTests): |
119 | """Signal hooking tests. |
120 | @@ -188,7 +190,7 @@ |
121 | |
122 | |
123 | class TestDataProcessingStatus(SafeTests): |
124 | - """Processes Status before sending it to SyncDaemon.""" |
125 | + """Process Status before sending it to SyncDaemon.""" |
126 | |
127 | @defer.inlineCallbacks |
128 | def test_get_status(self): |
129 | @@ -224,7 +226,7 @@ |
130 | |
131 | |
132 | class TestDataProcessingNameOwner(SafeTests): |
133 | - """Processes Name Owner data before sending it to SyncDaemon.""" |
134 | + """Process Name Owner data before sending it to SyncDaemon.""" |
135 | |
136 | def test_name_owner_changed_no_syncdaemon(self): |
137 | """Test name owner changed callback.""" |
138 | @@ -245,7 +247,7 @@ |
139 | |
140 | |
141 | class TestDataProcessingCQ(SafeTests): |
142 | - """Processes CQ data before sending it to SyncDaemon.""" |
143 | + """Process CQ data before sending it to SyncDaemon.""" |
144 | |
145 | @defer.inlineCallbacks |
146 | def test_nodata(self): |
147 | @@ -288,7 +290,7 @@ |
148 | |
149 | |
150 | class TestDataProcessingMQ(SafeTests): |
151 | - """Processes MQ data before sending it to SyncDaemon.""" |
152 | + """Process MQ data before sending it to SyncDaemon.""" |
153 | |
154 | @defer.inlineCallbacks |
155 | def test_nodata(self): |
156 | @@ -463,7 +465,7 @@ |
157 | |
158 | |
159 | class TestDataProcessingFolders(SafeTests): |
160 | - """Processes Folders data before sending it to SyncDaemon.""" |
161 | + """Process Folders data before sending it to SyncDaemon.""" |
162 | |
163 | @defer.inlineCallbacks |
164 | def test_nodata(self): |
165 | @@ -521,9 +523,29 @@ |
166 | self.get_msd_called("on_sd_folders_changed") |
167 | |
168 | |
169 | +class TestDataProcessingMetadata(SafeTests): |
170 | + """Process Metadata data before sending it to SyncDaemon.""" |
171 | + |
172 | + @defer.inlineCallbacks |
173 | + def test_info_ok(self): |
174 | + """Test get metadata and see response.""" |
175 | + md = dbus.Dictionary({'a': 3, 'c': 4}, signature=dbus.Signature('ss')) |
176 | + self.fake_sdt_response('get_metadata', md) |
177 | + rcv = yield self.dbus.get_metadata('path') |
178 | + self.assertEqual(rcv, dict(a=3, c=4)) |
179 | + |
180 | + @defer.inlineCallbacks |
181 | + def test_info_bad(self): |
182 | + """Test get metadata and get the error.""" |
183 | + exc = dbus.exceptions.DBusException( |
184 | + name='org.freedesktop.DBus.Python.KeyError') |
185 | + self.fake_sdt_response('get_metadata', exc) |
186 | + rcv = yield self.dbus.get_metadata('not a real path') |
187 | + self.assertEqual(rcv, dbusiface.NOT_SYNCHED_PATH) |
188 | + |
189 | |
190 | class TestDataProcessingShares(SafeTests): |
191 | - """Processes Shares data before sending it to SyncDaemon.""" |
192 | + """Process Shares data before sending it to SyncDaemon.""" |
193 | |
194 | @defer.inlineCallbacks |
195 | def test_sharestome_nodata(self): |
196 | @@ -720,6 +742,11 @@ |
197 | self.dbus.get_folders() |
198 | self.assertTrue(self.handler.check_inf("Getting folders")) |
199 | |
200 | + def test_get_metadata(self): |
201 | + """Test call to metadata.""" |
202 | + self.dbus.get_metadata('path') |
203 | + self.assertTrue(self.handler.check_inf("Getting metadata for u'path'")) |
204 | + |
205 | def test_get_shares_to_me(self): |
206 | """Test call to shares to me.""" |
207 | self.dbus.get_shares_to_me() |
208 | @@ -847,6 +874,15 @@ |
209 | self.assertTrue(self.handler.check_dbg(" Folders data: %r" % d)) |
210 | |
211 | @defer.inlineCallbacks |
212 | + def test_metadata_processing(self): |
213 | + """Test get metadata.""" |
214 | + d = dict(lot_of_data="I don't care") |
215 | + self.fake_sdt_response('get_metadata', d) |
216 | + yield self.dbus.get_metadata('path') |
217 | + self.assertTrue(self.handler.check_dbg( |
218 | + "Got metadata for path u'path': %r" % d)) |
219 | + |
220 | + @defer.inlineCallbacks |
221 | def test_sharestome_processing(self): |
222 | """Test get shares to me with one.""" |
223 | d = dict(accepted=u'True', access_level=u'View', free_bytes=u'123456', |
224 | @@ -877,7 +913,7 @@ |
225 | """Test the retry decorator.""" |
226 | |
227 | class Helper(object): |
228 | - """Fails some times, finally succeeds.""" |
229 | + """Fail some times, finally succeed.""" |
230 | def __init__(self, limit, excep=None): |
231 | self.cant = 0 |
232 | self.limit = limit |
233 | @@ -915,7 +951,7 @@ |
234 | self.assertTrue(dbusiface._is_retry_exception(err)) |
235 | |
236 | def get_decorated_func(self, func): |
237 | - """Executes the test calling the received function.""" |
238 | + """Execute the test calling the received function.""" |
239 | |
240 | @dbusiface.retryable |
241 | def f(): |
242 | |
243 | === modified file 'magicicada/tests/test_syncdaemon.py' |
244 | --- magicicada/tests/test_syncdaemon.py 2010-06-11 20:09:11 +0000 |
245 | +++ magicicada/tests/test_syncdaemon.py 2010-06-14 18:14:25 +0000 |
246 | @@ -133,7 +133,7 @@ |
247 | |
248 | @defer.inlineCallbacks |
249 | def test_initial_value(self): |
250 | - """Fills the status info initially.""" |
251 | + """Fill the status info initially.""" |
252 | called = [] |
253 | def fake(): |
254 | """Fake method.""" |
255 | @@ -167,7 +167,7 @@ |
256 | return deferred |
257 | |
258 | def test_status_changed_affects_cuurent_status(self): |
259 | - """Makes changes to see how status are reflected.""" |
260 | + """Make changes to see how status are reflected.""" |
261 | # one set of values |
262 | self.sd.on_sd_status_changed('name1', 'description1', False, True, |
263 | False, 'queues1', 'connection1') |
264 | @@ -261,7 +261,7 @@ |
265 | |
266 | @defer.inlineCallbacks |
267 | def test_initial_value(self): |
268 | - """Fills the content queue info initially.""" |
269 | + """Fill the content queue info initially.""" |
270 | called = [] |
271 | self.sd.dbus.get_content_queue = lambda: called.append(True) |
272 | yield self.sd._get_initial_data() |
273 | @@ -341,7 +341,7 @@ |
274 | |
275 | @defer.inlineCallbacks |
276 | def test_initial_value(self): |
277 | - """Fills the meta queue info initially.""" |
278 | + """Fill the meta queue info initially.""" |
279 | called = [] |
280 | def f(): |
281 | """Helper function.""" |
282 | @@ -777,11 +777,38 @@ |
283 | self.assertTrue(self.hdlr.check_inf("SD Name Owner changed: True")) |
284 | |
285 | |
286 | +class MetadataTests(BaseTest): |
287 | + """Get Metadata info.""" |
288 | + |
289 | + def test_get_metadata_no_callback_set(self): |
290 | + """It's mandatory to set the callback for this response.""" |
291 | + self.assertRaises(ValueError, self.sd.get_metadata, 'path') |
292 | + |
293 | + def test_get_metadata_ok(self): |
294 | + """Get the metadata for given path.""" |
295 | + called = [] |
296 | + self.sd.dbus.get_metadata = lambda p: defer.succeed('foo') |
297 | + self.sd.on_metadata_ready_callback = lambda *a: called.extend(a) |
298 | + self.sd.get_metadata('path') |
299 | + self.assertEqual(called, ['path', 'foo']) |
300 | + |
301 | + def test_get_metadata_double(self): |
302 | + """Get the metadata twice.""" |
303 | + called = [] |
304 | + fake_md = {'path1': 'foo', 'path2': 'bar'} |
305 | + self.sd.dbus.get_metadata = lambda p: defer.succeed(fake_md[p]) |
306 | + self.sd.on_metadata_ready_callback = lambda *a: called.append(a) |
307 | + self.sd.get_metadata('path1') |
308 | + self.sd.get_metadata('path2') |
309 | + self.assertEqual(called[0], ('path1', 'foo')) |
310 | + self.assertEqual(called[1], ('path2', 'bar')) |
311 | + |
312 | + |
313 | class FoldersTests(BaseTest): |
314 | """Folders checking.""" |
315 | |
316 | def test_foldercreated_callback(self): |
317 | - """Gets the new data after the folders changed.""" |
318 | + """Get the new data after the folders changed.""" |
319 | # set the callback |
320 | called = [] |
321 | self.sd.dbus.get_folders = lambda: called.append(True) |
322 | @@ -794,7 +821,7 @@ |
323 | |
324 | @defer.inlineCallbacks |
325 | def test_initial_value(self): |
326 | - """Fills the folder info initially.""" |
327 | + """Fill the folder info initially.""" |
328 | called = [] |
329 | self.sd.dbus.get_folders = lambda: called.append(True) |
330 | yield self.sd._get_initial_data() |
331 | @@ -817,7 +844,7 @@ |
332 | """Shares checking.""" |
333 | |
334 | def test_shares_changed_callback(self): |
335 | - """Gets the new data after the shares changed.""" |
336 | + """Get the new data after the shares changed.""" |
337 | # set the callback |
338 | called = [] |
339 | self.sd.dbus.get_shares_to_me = lambda: called.append(True) |
340 | @@ -831,7 +858,7 @@ |
341 | |
342 | @defer.inlineCallbacks |
343 | def test_initial_value(self): |
344 | - """Fills the folder info initially.""" |
345 | + """Fill the folder info initially.""" |
346 | called = [] |
347 | self.sd.dbus.get_shares_to_me = lambda: called.append(True) |
348 | self.sd.dbus.get_shares_to_others = lambda: called.append(True) |
Docstring """Processes...""" needs fixing to """Process""". Same for "Gets" and "Returns".