Author: Maciej Fijalkowski <[email protected]>
Branch:
Changeset: r65132:1d6d177a0c6e
Date: 2013-07-01 08:49 +0200
http://bitbucket.org/pypy/pypy/changeset/1d6d177a0c6e/
Log: (andrewchambers) merge identity-set. This branch adds a set based on
identity, just like identity dict.
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -26,7 +26,7 @@
def __repr__(self):
"""representation for debugging purposes"""
reprlist = [repr(w_item) for w_item in self.getkeys()]
- return "<%s(%s)>" % (self.__class__.__name__, ', '.join(reprlist))
+ return "<%s(%s)(%s)>" % (self.__class__.__name__, self.strategy, ',
'.join(reprlist))
def from_storage_and_strategy(self, storage, strategy):
obj = self._newobj(self.space, None)
@@ -780,6 +780,8 @@
strategy = self.space.fromcache(StringSetStrategy)
elif type(w_key) is W_UnicodeObject:
strategy = self.space.fromcache(UnicodeSetStrategy)
+ elif self.space.type(w_key).compares_by_identity():
+ strategy = self.space.fromcache(IdentitySetStrategy)
else:
strategy = self.space.fromcache(ObjectSetStrategy)
w_set.strategy = strategy
@@ -1336,6 +1338,41 @@
break
d_obj[w_item] = None
+class IdentitySetStrategy(AbstractUnwrappedSetStrategy, SetStrategy):
+ erase, unerase = rerased.new_erasing_pair("identityset")
+ erase = staticmethod(erase)
+ unerase = staticmethod(unerase)
+
+ def get_empty_storage(self):
+ return self.erase({})
+
+ def get_empty_dict(self):
+ return {}
+
+ def is_correct_type(self, w_key):
+ w_type = self.space.type(w_key)
+ return w_type.compares_by_identity()
+
+ def may_contain_equal_elements(self, strategy):
+ #empty first, probably more likely
+ if strategy is self.space.fromcache(EmptySetStrategy):
+ return False
+ if strategy is self.space.fromcache(IntegerSetStrategy):
+ return False
+ if strategy is self.space.fromcache(StringSetStrategy):
+ return False
+ if strategy is self.space.fromcache(UnicodeSetStrategy):
+ return False
+ return True
+
+ def unwrap(self, w_item):
+ return w_item
+
+ def wrap(self, item):
+ return item
+
+ def iter(self, w_set):
+ return IdentityIteratorImplementation(self.space, self, w_set)
class IteratorImplementation(object):
def __init__(self, space, strategy, implementation):
@@ -1427,6 +1464,17 @@
else:
return None
+class IdentityIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, strategy, w_set):
+ IteratorImplementation.__init__(self, space, strategy, w_set)
+ d = strategy.unerase(w_set.sstorage)
+ self.iterator = d.iterkeys()
+
+ def next_entry(self):
+ for key in self.iterator:
+ return self.space.wrap(key)
+ else:
+ return None
class RDictIteratorImplementation(IteratorImplementation):
def __init__(self, space, strategy, w_set):
@@ -1545,6 +1593,15 @@
w_set.strategy = space.fromcache(UnicodeSetStrategy)
w_set.sstorage = w_set.strategy.get_storage_from_list(iterable_w)
return
+
+ # check for compares by identity
+ for w_item in iterable_w:
+ if not space.type(w_item).compares_by_identity():
+ break
+ else:
+ w_set.strategy = space.fromcache(IdentitySetStrategy)
+ w_set.sstorage = w_set.strategy.get_storage_from_list(iterable_w)
+ return
w_set.strategy = space.fromcache(ObjectSetStrategy)
w_set.sstorage = w_set.strategy.get_storage_from_list(iterable_w)
diff --git a/pypy/objspace/std/test/test_identityset.py
b/pypy/objspace/std/test/test_identityset.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_identityset.py
@@ -0,0 +1,209 @@
+import py
+
+
+
+class AppTestIdentitySet(object):
+
+ #needed for compares_by_identity
+ spaceconfig = {"objspace.std.withidentitydict": True}
+
+ def setup_class(cls):
+ from pypy.objspace.std import identitydict
+ if cls.runappdirect:
+ py.test.skip("interp2app doesn't work on appdirect")
+
+ def w_uses_strategy(self, s , obj):
+ import __pypy__
+ return s in __pypy__.internal_repr(obj)
+
+ def test_use_identity_strategy(self):
+
+ class Plain(object):
+ pass
+
+ class CustomEq(object):
+ def __eq__(self, other):
+ return True
+
+ class CustomCmp (object):
+ def __cmp__(self, other):
+ return 0
+
+ class CustomHash(object):
+ def __hash__(self):
+ return 0
+
+ s = set()
+
+ assert not self.uses_strategy('IdentitySetStrategy',s)
+
+ s.add(Plain())
+
+ assert self.uses_strategy('IdentitySetStrategy',s)
+
+ for cls in [CustomEq,CustomCmp,CustomHash]:
+ s = set()
+ s.add(cls())
+ assert not self.uses_strategy('IdentitySetStrategy',s)
+
+
+ def test_use_identity_strategy_list(self):
+
+ class X(object):
+ pass
+
+ assert self.uses_strategy('IdentitySetStrategy',set([X(),X()]))
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),""]))
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),u""]))
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),1]))
+
+ def test_identity_strategy_add(self):
+
+ class X(object):
+ pass
+
+ class NotIdent(object):
+ def __eq__(self,other):
+ pass
+
+ s = set([X(),X()])
+ s.add('foo')
+ assert not self.uses_strategy('IdentitySetStrategy',s)
+ s = set([X(),X()])
+ s.add(NotIdent())
+ assert not self.uses_strategy('IdentitySetStrategy',s)
+
+ def test_identity_strategy_sanity(self):
+
+ class X(object):
+ pass
+
+ class Y(object):
+ pass
+
+ a,b,c,d,e,f = X(),Y(),X(),Y(),X(),Y()
+
+ s = set([a,b]).union(set([c]))
+ assert self.uses_strategy('IdentitySetStrategy',s)
+ assert set([a,b,c]) == s
+ s = set([a,b,c,d,e,f]) - set([d,e,f])
+ assert self.uses_strategy('IdentitySetStrategy',s)
+ assert set([a,b,c]) == s
+
+
+ s = set([a])
+ s.update([b,c])
+
+ assert s == set([a,b,c])
+
+
+ def test_identity_strategy_iterators(self):
+
+ class X(object):
+ pass
+
+ s = set([X() for i in range(10)])
+ counter = 0
+ for item in s:
+ counter += 1
+ assert item in s
+
+ assert counter == 10
+
+
+ def test_identity_strategy_other_cmp(self):
+
+ #test tries to hit positive and negative in
+ # may_contain_equal_elements
+
+ class X(object):
+ pass
+
+ s = set([X() for i in range(10)])
+
+ assert s.intersection(set([1,2,3])) == set()
+ assert s.intersection(set(['a','b','c'])) == set()
+ assert s.intersection(set(['a','b','c'])) == set()
+ assert s.intersection(set([X(),X()])) == set()
+
+ other = set(['a','b','c',s.__iter__().next()])
+ intersect = s.intersection(other)
+ assert len(intersect) == 1
+ assert intersect.__iter__().next() in s
+ assert intersect.__iter__().next() in other
+
+ def test_class_monkey_patch(self):
+
+ class X(object):
+ pass
+
+ s = set()
+
+ s.add(X())
+ assert self.uses_strategy('IdentitySetStrategy',s)
+ X.__eq__ = lambda self,other : None
+ s.add(X())
+ assert not self.uses_strategy('IdentitySetStrategy',s)
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),X()]))
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),""]))
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),u""]))
+ assert not self.uses_strategy('IdentitySetStrategy',set([X(),1]))
+
+ # An interesting case, add an instance, mutate the class,
+ # then add the same instance.
+
+ class X(object):
+ pass
+
+ s = set()
+ inst = X()
+ s.add(inst)
+ X.__eq__ = lambda x,y : x is y
+ s.add(inst)
+
+ assert len(s) == 1
+ assert s.__iter__().next() is inst
+ assert not self.uses_strategy('IdentitySetStrategy',s)
+
+
+ #Add instance, mutate class, check membership of that instance.
+
+ class X(object):
+ pass
+
+
+ inst = X()
+ s = set()
+ s.add(inst)
+ X.__eq__ = lambda x,y : x is y
+ assert inst in s
+
+ # Test Wrong strategy
+ # If the strategy is changed by mutation, but the instance
+ # does not change, then this tests the methods that call
+ # may_contain_equal_elements still function.
+ # i.e. same instance in two sets, one with object strategy, one with
+ # identity strategy.
+
+ class X(object):
+ pass
+
+
+ inst = X()
+ s1 = set()
+ s1.add(inst)
+ assert self.uses_strategy('IdentitySetStrategy',s1)
+ X.__eq__ = lambda x,y : x is y
+ s2 = set()
+ s2.add(inst)
+ assert not self.uses_strategy('IdentitySetStrategy',s2)
+
+ assert s1.intersection(s2) == set([inst])
+ assert (s1 - s2) == set()
+ assert (s2 - s1) == set()
+
+ s1.difference_update(s2)
+ assert s1 == set()
+
+
+
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit