Merge lp:~mandel/desktopcouch/fix_bug_519873 into lp:desktopcouch

Proposed by Manuel de la Peña
Status: Merged
Approved by: Chad Miller
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~mandel/desktopcouch/fix_bug_519873
Merge into: lp:desktopcouch
Diff against target: 142 lines (+105/-0)
2 files modified
desktopcouch/records/record.py (+43/-0)
desktopcouch/records/tests/test_record.py (+62/-0)
To merge this branch: bzr merge lp:~mandel/desktopcouch/fix_bug_519873
Reviewer Review Type Date Requested Status
John O'Brien (community) Approve
Chad Miller (community) Approve
Review via email: mp+19018@code.launchpad.net

Commit message

Make list-like MergableList objects behave more like lists by adding pop() and remove() methods. (LP: #519873)

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

As explained in bug 519873 the remove and pop methods are missing. Although the optimal solution for this kind of problem would be implement a Bimap for the MergeableList this is not feseable because python lists and dicts are not hashable. This solution provides a O(n*m) performance where n is the number of objects in the mergeable list and m is the size of the possible inner lists.

Revision history for this message
Chad Miller (cmiller) wrote :

"for index, current_value in enumerate(self):" is preferable to "index = 0", then "for current_value in..." and "index += 1" inside.

Also, I'm not too enthusiastic about restricting pop/remove on the last item. It's not a problem for lists to remove the last item. Perhaps we should destroy the container that must not be empty instead, when it reaches zero entries. Or, change the container so that it may be empty? (I do not understand the empty restriction yet.)

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

The empty restriction is coming from the RecordData class that does not allow to add empty lists so we do not allow to add them and then delete all its contents. I know there is a bug files regarding this (https://bugs.launchpad.net/desktopcouch/+bug/510232) maybe fixing that bug and then merging this branch without the last pop and remove check is a better idea.

Revision history for this message
Eric Casteleijn (thisfred) wrote :

I suggested a solution to https://bugs.launchpad.net/desktopcouch/+bug/510232 on that bug's page, which I agree should probably be fixed first, which would make a nicer solution possible here. (Also not all lists would be mergeable lists, so any performance hit for very large lists would be circumventable by not making them mergeable lists.)

Revision history for this message
Chad Miller (cmiller) :
review: Approve
Revision history for this message
John O'Brien (jdobrien) wrote :

The code looks good but I get unrelated errors.

======================================================================
FAIL: test_get_my_host_unique_id
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 106, in maybeDeferred
    result = f(*args, **kw)
  File "/usr/lib/python2.6/dist-packages/twisted/internet/utils.py", line 191, in runWithWarningsSuppressed
    result = f(*a, **kw)
  File "/home/john/canonical/ubuntuone/desktopcouch/fix_bug_519873/desktopcouch/pair/tests/test_couchdb_io.py", line 134, in test_get_my_host_unique_id
    self.assertEquals(got, again)
  File "/usr/lib/python2.6/dist-packages/twisted/trial/unittest.py", line 262, in failUnlessEqual
    % (msg, pformat(first), pformat(second)))
FailTest: not equal:
a = ['a0624880-6e12-4794-8010-32a973c43c8f']
b = ['5946ee98-d83f-479e-ba0f-3f88bfa11a6c']

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

Interesting, are you running Lucid? I ran the tests on Karmic and everything was find there.. Let me know so I pump my OS to try and find the error.

> The code looks good but I get unrelated errors.
>
>
> ======================================================================
> FAIL: test_get_my_host_unique_id
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 106,
> in maybeDeferred
> result = f(*args, **kw)
> File "/usr/lib/python2.6/dist-packages/twisted/internet/utils.py", line 191,
> in runWithWarningsSuppressed
> result = f(*a, **kw)
> File "/home/john/canonical/ubuntuone/desktopcouch/fix_bug_519873/desktopcouc
> h/pair/tests/test_couchdb_io.py", line 134, in test_get_my_host_unique_id
> self.assertEquals(got, again)
> File "/usr/lib/python2.6/dist-packages/twisted/trial/unittest.py", line 262,
> in failUnlessEqual
> % (msg, pformat(first), pformat(second)))
> FailTest: not equal:
> a = ['a0624880-6e12-4794-8010-32a973c43c8f']
> b = ['5946ee98-d83f-479e-ba0f-3f88bfa11a6c']

Revision history for this message
Chad Miller (cmiller) wrote :

Attempt to merge lp:~mandel/desktopcouch/fix_bug_519873 into lp:desktopcouch failed due to merge conflicts:

text conflict in bin/desktopcouch-stop

Revision history for this message
Chad Miller (cmiller) wrote :

Attempt to merge lp:~mandel/desktopcouch/fix_bug_519873 into lp:desktopcouch failed due to merge conflicts:

text conflict in bin/desktopcouch-stop

125. By Manuel de la Peña

Merged with current trunk

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

The merge should be possible now. As a side note since I have been using Lucid I get the fails that John reported.
> Attempt to merge lp:~mandel/desktopcouch/fix_bug_519873 into lp:desktopcouch
> failed due to merge conflicts:
>
> text conflict in bin/desktopcouch-stop

Revision history for this message
Chad Miller (cmiller) wrote :

Tarmac has no memory. When it marks a failed merge as Needs Review, it doesn't try again until someone marks the branch Approved again.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/desktopcouch-stop' (properties changed: +x to -x)
=== modified file 'desktopcouch/records/record.py'
--- desktopcouch/records/record.py 2010-01-22 20:52:35 +0000
+++ desktopcouch/records/record.py 2010-02-25 22:39:13 +0000
@@ -198,6 +198,11 @@
198 def __contains__(self, key):198 def __contains__(self, key):
199 return key in self._data199 return key in self._data
200200
201 def __cmp__(self, value):
202 if isinstance(value, RecordDict):
203 return cmp(self._data, value._data)
204 return -1
205
201 def get(self, key, default=None):206 def get(self, key, default=None):
202 """Override get method."""207 """Override get method."""
203 if not key in self._data:208 if not key in self._data:
@@ -271,6 +276,24 @@
271 value = value._data276 value = value._data
272 return value in self._data.values()277 return value in self._data.values()
273278
279 def __cmp__(self, value):
280 """
281 Implement the compare to be able to compare with other mergeable
282 lists, tuples and lists.
283 """
284 if (hasattr(value, "__iter__")
285 and hasattr(value, "__getitem__")
286 and hasattr(value, "__len__")):
287 # we should have the same value in the same order
288 length = len(value)
289 if len(self) == length:
290 for index, current_value in enumerate(self):
291 cmp_value = cmp(self[index], value[index])
292 if cmp_value != 0:
293 return cmp_value
294 return 0
295 return -1
296
274 def _get_ordered_keys(self):297 def _get_ordered_keys(self):
275 """Get list of uuid keys ordered by 'order' property or uuid key."""298 """Get list of uuid keys ordered by 'order' property or uuid key."""
276 result = []299 result = []
@@ -299,6 +322,26 @@
299 new_uuid)322 new_uuid)
300 super(MergeableList, self).__setitem__(new_uuid, value)323 super(MergeableList, self).__setitem__(new_uuid, value)
301324
325 def remove(self, value):
326 if len(self) == 1:
327 raise ValueError("MergeableList cannot be empty.")
328 index = 0
329 for current_value in self:
330 # important! use the data in self first 'cause mergeable lists
331 # can be compared with list and tuples but no the other way around
332 if cmp(current_value, value) == 0:
333 del self[index]
334 return
335 index += 1
336 raise ValueError("list.remove(x): x not in list")
337
338 def pop(self, index):
339 if len(self) == 1:
340 raise ValueError("MergeableList cannot be empty.")
341 value = self[index]
342 del self[index]
343 return value
344
302 def index(self, key):345 def index(self, key):
303 """Get value by index."""346 """Get value by index."""
304 return self.__getitem__(key)347 return self.__getitem__(key)
305348
=== modified file 'desktopcouch/records/tests/test_record.py'
--- desktopcouch/records/tests/test_record.py 2010-01-29 22:14:25 +0000
+++ desktopcouch/records/tests/test_record.py 2010-02-25 22:39:13 +0000
@@ -177,7 +177,69 @@
177 self.record["subfield_uuid"].append(value)177 self.record["subfield_uuid"].append(value)
178 self.assertEqual(3, len(self.record["subfield_uuid"]))178 self.assertEqual(3, len(self.record["subfield_uuid"]))
179 self.assertEqual("value32", self.record["subfield_uuid"][-1]["field32"])179 self.assertEqual("value32", self.record["subfield_uuid"][-1]["field32"])
180
181 def test_mergeable_list_remove(self):
182 """Test remove a normal value"""
183 value = "string"
184 self.record["subfield_uuid"].append(value)
185 self.record["subfield_uuid"].remove(value)
186 self.assertFalse(value in self.record["subfield_uuid"])
187
188 def test_mergeable_list_remove_record_dict(self):
189 """Test remove a RecordDict value"""
190 value = RecordDict({
191 "field31": "value31",
192 "field32": "value32"})
193 self.record["subfield_uuid"].append(value)
194 self.record["subfield_uuid"].remove(value)
195 self.assertFalse(value in self.record["subfield_uuid"])
180196
197 def test_mergeable_list_remove_list(self):
198 """Test list removal"""
199 value = [1, 2, 3, 4, 5]
200 self.record["subfield_uuid"].append(value)
201 self.record["subfield_uuid"].remove(value)
202 self.assertFalse(value in self.record["subfield_uuid"])
203
204 def test_mergeable_list_remove_tuple(self):
205 """Test tuple removal"""
206 value = (1, 2, 3, 4, 5)
207 self.record["subfield_uuid"].append(value)
208 self.record["subfield_uuid"].remove(value)
209 self.assertFalse(value in self.record["subfield_uuid"])
210
211 def test_mergeable_list_remove_missing(self):
212 """Test missing data rmeoval"""
213 self.assertRaises(ValueError, self.record["subfield_uuid"].remove,
214 "missing_data")
215 def test_mergeable_list_remove_last(self):
216 """Test that exception is raised when removing last item."""
217 self.record["subfield_uuid"] = [1]
218 self.assertRaises(ValueError, self.record["subfield_uuid"].remove, 1)
219
220 def test_mergeable_list_pop_correct_index(self):
221 """Test the pop method when working with a correct index."""
222 value = [1, 2, 3, 4, 5]
223 self.record["subfield_uuid"] = value
224 # test with negative index
225 poped_value = self.record["subfield_uuid"].pop(-2)
226 self.assertEqual(value[-2], poped_value)
227 # test with positive index
228 poped_value = self.record["subfield_uuid"].pop(1)
229 self.assertEqual(value[1], poped_value)
230
231 def test_mergeable_list_pop_wrong_index(self):
232 """Test pop when index is out or range."""
233 value = [1, 2, 3, 4, 5]
234 self.record["subfield_uuid"] = value
235 self.assertRaises(IndexError, self.record["subfield_uuid"].pop,
236 len(value)*2)
237
238 def test_mergeable_list_pop_last(self):
239 """Test that exception is raised when poping last item"""
240 self.record["subfield_uuid"] = [1]
241 self.assertRaises(ValueError, self.record["subfield_uuid"].pop, 0)
242
181 def test_tuple(self):243 def test_tuple(self):
182 """Test assigning tuples to a key results in mergeable lists."""244 """Test assigning tuples to a key results in mergeable lists."""
183 rec = Record({'record_type': 'http://fnord.org/smorgasbord'})245 rec = Record({'record_type': 'http://fnord.org/smorgasbord'})

Subscribers

People subscribed via source and target branches