Skip to content

Commit ee9d6b9

Browse files
committed
util: Added test for iterable list, and implemented __contains__ and __del__ functionality
1 parent 4772fe0 commit ee9d6b9

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

git/test/test_util.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,20 @@
2020
to_bin_sha,
2121
NULL_HEX_SHA,
2222
LockedFD,
23-
Actor
23+
Actor,
24+
IterableList
2425
)
2526

2627

28+
class TestIterableMember(object):
29+
"""A member of an iterable list"""
30+
__slots__ = ("name", "prefix_name")
31+
32+
def __init__(self, name):
33+
self.name = name
34+
self.prefix_name = name
35+
36+
2737
class TestUtils(TestBase):
2838
def setup(self):
2939
self.testdict = {
@@ -203,6 +213,53 @@ def test_lockedfd(self):
203213
self.fail("expected OSError")
204214
# END handle exceptions
205215

216+
def test_iterable_list(self):
217+
for args in (('name',), ('name', 'prefix_')):
218+
l = IterableList('name')
219+
220+
m1 = TestIterableMember('one')
221+
m2 = TestIterableMember('two')
222+
223+
l.extend((m1, m2))
224+
225+
assert len(l) == 2
226+
227+
# contains works with name and identity
228+
assert m1.name in l
229+
assert m2.name in l
230+
assert m2 in l
231+
assert m2 in l
232+
assert 'invalid' not in l
233+
234+
# with string index
235+
assert l[m1.name] is m1
236+
assert l[m2.name] is m2
237+
238+
# with int index
239+
assert l[0] is m1
240+
assert l[1] is m2
241+
242+
# with getattr
243+
assert l.one is m1
244+
assert l.two is m2
245+
246+
# test exceptions
247+
self.failUnlessRaises(AttributeError, getattr, l, 'something')
248+
self.failUnlessRaises(IndexError, l.__getitem__, 'something')
249+
250+
# delete by name and index
251+
self.failUnlessRaises(IndexError, l.__delitem__, 'something')
252+
del(l[m2.name])
253+
assert len(l) == 1
254+
assert m2.name not in l and m1.name in l
255+
del(l[0])
256+
assert m1.name not in l
257+
assert len(l) == 0
258+
259+
self.failUnlessRaises(IndexError, l.__delitem__, 0)
260+
self.failUnlessRaises(IndexError, l.__delitem__, 'something')
261+
#END for each possible mode
262+
206263

207264
class TestActor(TestBase):
208265
def test_from_string_should_separate_name_and_email(self):

git/util.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,21 @@ def __init__(self, id_attr, prefix=''):
734734
raise ValueError("First parameter must be a string identifying the name-property. Extend the list after initialization")
735735
# END help debugging !
736736

737+
def __contains__(self, attr):
738+
# first try identy match for performance
739+
rval = list.__contains__(self, attr)
740+
if rval:
741+
return rval
742+
#END handle match
743+
744+
# otherwise make a full name search
745+
try:
746+
getattr(self, attr)
747+
return True
748+
except (AttributeError, TypeError):
749+
return False
750+
#END handle membership
751+
737752
def __getattr__(self, attr):
738753
attr = self._prefix + attr
739754
for item in self:
@@ -750,7 +765,24 @@ def __getitem__(self, index):
750765
return getattr(self, index)
751766
except AttributeError:
752767
raise IndexError( "No item found with id %r" % (self._prefix + index) )
768+
# END handle getattr
753769

770+
def __delitem__(self, index):
771+
delindex = index
772+
if not isinstance(index, int):
773+
delindex = -1
774+
name = self._prefix + index
775+
for i, item in enumerate(self):
776+
if getattr(item, self._id_attr) == name:
777+
delindex = i
778+
break
779+
#END search index
780+
#END for each item
781+
if delindex == -1:
782+
raise IndexError("Item with name %s not found" % name)
783+
#END handle error
784+
#END get index to delete
785+
list.__delitem__(self, delindex)
754786

755787

756788
#} END utilities

0 commit comments

Comments
 (0)