[pypy-svn] r27644 - in pypy/dist/pypy/rpython: ootypesystem test

antocuni at codespeak.net antocuni at codespeak.net
Wed May 24 13:22:52 CEST 2006


Author: antocuni
Date: Wed May 24 13:22:36 2006
New Revision: 27644

Modified:
   pypy/dist/pypy/rpython/ootypesystem/ootype.py
   pypy/dist/pypy/rpython/ootypesystem/rdict.py
   pypy/dist/pypy/rpython/test/test_rdict.py
Log:
Some refactoring of test_rdict, and some little bugs fixed.



Modified: pypy/dist/pypy/rpython/ootypesystem/ootype.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/ootype.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/ootype.py	Wed May 24 13:22:36 2006
@@ -809,6 +809,12 @@
         self._str = value
         self._TYPE = STRING
 
+    def __hash__(self):
+        return hash(self._str)
+
+    def __cmp__(self, other):
+        return cmp(self._str, other._str)
+
     def ll_stritem_nonneg(self, i):
         # NOT_RPYTHON
         assert i >= 0
@@ -987,7 +993,7 @@
         return self._dict[key]
 
     def ll_set(self, key, value):
-        # NOT_RPYTHON        
+        # NOT_RPYTHON
         assert typeOf(key) == self._TYPE._KEYTYPE
         assert typeOf(value) == self._TYPE._VALUETYPE
         self._dict[key] = value
@@ -1076,6 +1082,13 @@
         else:
             return 0 # for all null tuples
 
+    def __hash__(self):
+        key = tuple(self._items.keys()), tuple(self._items.values())
+        return hash(key)
+
+    def __eq__(self, other):
+        return self._items == other._items
+
 class _null_record(_null_mixin(_record), _record):
 
     def __init__(self, RECORD):

Modified: pypy/dist/pypy/rpython/ootypesystem/rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/ootypesystem/rdict.py	(original)
+++ pypy/dist/pypy/rpython/ootypesystem/rdict.py	Wed May 24 13:22:36 2006
@@ -137,7 +137,7 @@
 
     def convert_const(self, dictobj):
         if dictobj is None:
-            return lltype.nullptr(self.DICT)
+            return self.DICT._defl()
         if not isinstance(dictobj, dict):
             raise TyperError("expected a dict: %r" % (dictobj,))
         try:
@@ -180,7 +180,7 @@
 ##            hop.exception_is_here()
 ##        else:
 ##            hop.exception_cannot_occur()
-        hop.exception_is_here()
+##        hop.exception_is_here()
         return r_dict.send_message(hop, 'll_set', can_raise=True)
 
     def rtype_contains((r_dict, r_key), hop):

Modified: pypy/dist/pypy/rpython/test/test_rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rdict.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rdict.py	Wed May 24 13:22:36 2006
@@ -3,461 +3,11 @@
 from pypy.rpython.test.test_llinterp import interpret, interpret_raises
 from pypy.rpython import rint
 from pypy.rpython.lltypesystem import rdict, rstr
+from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
 
 import py
 py.log.setconsumer("rtyper", py.log.STDOUT)
 
-class BaseTestDictRtyping:
-    def interpret(self, fn, args):
-        return interpret(fn, args, type_system=self.ts)
-
-    def interpret_raises(self, exc, fn, args):
-        return interpret_raises(exc, fn, args, type_system=self.ts)
-
-
-# XXX: most tests doesn't works because ootypesystem doesn't support strings, yet
-def test_dict_creation():
-    def createdict(i): 
-        d = {'hello' : i}
-        return d['hello']
-
-    res = interpret(createdict, [42])
-    assert res == 42
-
-def test_dict_getitem_setitem(): 
-    def func(i): 
-        d = {'hello' : i}
-        d['world'] = i + 1
-        return d['hello'] * d['world'] 
-    res = interpret(func, [6])
-    assert res == 42
-
-def test_dict_getitem_keyerror(): 
-    def func(i): 
-        d = {'hello' : i}
-        try:
-            return d['world']
-        except KeyError:
-            return 0 
-    res = interpret(func, [6])
-    assert res == 0
-
-def test_dict_but_not_with_char_keys():
-    def func(i):
-        d = {'h': i}
-        try:
-            return d['hello']
-        except KeyError:
-            return 0
-    res = interpret(func, [6])
-    assert res == 0
-
-def test_dict_del_simple():
-    def func(i): 
-        d = {'hello' : i}
-        d['world'] = i + 1
-        del d['hello']
-        return len(d) 
-    res = interpret(func, [6])
-    assert res == 1
-
-def test_dict_clear():
-    def func(i):
-        d = {'abc': i}
-        d['def'] = i+1
-        d.clear()
-        d['ghi'] = i+2
-        return ('abc' not in d and 'def' not in d
-                and d['ghi'] == i+2 and len(d) == 1)
-    res = interpret(func, [7])
-    assert res == True
-
-def test_empty_strings():
-    def func(i): 
-        d = {'' : i}
-        del d['']
-        try:
-            d['']
-            return 0
-        except KeyError:
-            pass
-        return 1
-    res = interpret(func, [6])
-    assert res == 1
-    
-    def func(i): 
-        d = {'' : i}
-        del d['']
-        d[''] = i + 1
-        return len(d)
-    res = interpret(func, [6])
-    assert res == 1
-
-def test_deleted_entry_reusage_with_colliding_hashes(): 
-    def lowlevelhash(value): 
-        p = lltype.malloc(rstr.STR, len(value))
-        for i in range(len(value)):
-            p.chars[i] = value[i]
-        return rstr.LLHelpers.ll_strhash(p) 
-    
-    def func(c1, c2): 
-        c1 = chr(c1) 
-        c2 = chr(c2) 
-        d = {}
-        d[c1] = 1
-        d[c2] = 2 
-        del d[c1]
-        return d[c2]
-
-    char_by_hash = {}
-    base = rdict.DICT_INITSIZE
-    for y in range(0, 256):
-        y = chr(y)
-        y_hash = lowlevelhash(y) % base 
-        char_by_hash.setdefault(y_hash, []).append(y)
-
-    x, y = char_by_hash[0][:2]   # find a collision
-       
-    res = interpret(func, [ord(x), ord(y)])
-    assert res == 2
-
-    def func2(c1, c2): 
-        c1 = chr(c1) 
-        c2 = chr(c2) 
-        d = {}
-        d[c1] = 1
-        d[c2] = 2 
-        del d[c1]
-        d[c1] = 3
-        return d 
-
-    res = interpret(func2, [ord(x), ord(y)])
-    for i in range(len(res.entries)): 
-        assert not (res.entries[i].everused() and not res.entries[i].valid())
-
-    def func3(c0, c1, c2, c3, c4, c5, c6, c7):
-        d = {}
-        c0 = chr(c0) ; d[c0] = 1; del d[c0]
-        c1 = chr(c1) ; d[c1] = 1; del d[c1]
-        c2 = chr(c2) ; d[c2] = 1; del d[c2]
-        c3 = chr(c3) ; d[c3] = 1; del d[c3]
-        c4 = chr(c4) ; d[c4] = 1; del d[c4]
-        c5 = chr(c5) ; d[c5] = 1; del d[c5]
-        c6 = chr(c6) ; d[c6] = 1; del d[c6]
-        c7 = chr(c7) ; d[c7] = 1; del d[c7]
-        return d
-
-    if rdict.DICT_INITSIZE != 8: 
-        py.test.skip("make dict tests more indepdent from initsize")
-    res = interpret(func3, [ord(char_by_hash[i][0]) 
-                               for i in range(rdict.DICT_INITSIZE)])
-    count_frees = 0
-    for i in range(len(res.entries)):
-        if not res.entries[i].everused():
-            count_frees += 1
-    assert count_frees >= 3
-
-def test_dict_resize():
-    def func(want_empty):
-        d = {}
-        for i in range(rdict.DICT_INITSIZE):
-            d[chr(ord('a') + i)] = i
-        if want_empty:
-            for i in range(rdict.DICT_INITSIZE):
-                del d[chr(ord('a') + i)]
-        return d
-    res = interpret(func, [0])
-    assert len(res.entries) > rdict.DICT_INITSIZE 
-    res = interpret(func, [1])
-    assert len(res.entries) == rdict.DICT_INITSIZE 
-
-def test_dict_valid_resize():
-    # see if we find our keys after resize
-    def func():
-        d = {}
-        # fill it up
-        for i in range(10):
-            d[str(i)] = 0
-        # delete again
-        for i in range(10):
-            del d[str(i)]
-        res = 0
-    # if it does not crash, we are fine. It crashes if you forget the hash field.
-    interpret(func, [])
-
-def test_dict_iteration():
-    def func(i, j):
-        d = {}
-        d['hello'] = i
-        d['world'] = j
-        k = 1
-        for key in d:
-            k = k * d[key]
-        return k
-    res = interpret(func, [6, 7])
-    assert res == 42
-
-def test_dict_itermethods():
-    def func():
-        d = {}
-        d['hello'] = 6
-        d['world'] = 7
-        k1 = k2 = k3 = 1
-        for key in d.iterkeys():
-            k1 = k1 * d[key]
-        for value in d.itervalues():
-            k2 = k2 * value
-        for key, value in d.iteritems():
-            assert d[key] == value
-            k3 = k3 * value
-        return k1 + k2 + k3
-    res = interpret(func, [])
-    assert res == 42 + 42 + 42
-
-def test_two_dicts_with_different_value_types():
-    def func(i):
-        d1 = {}
-        d1['hello'] = i + 1
-        d2 = {}
-        d2['world'] = d1 
-        return d2['world']['hello'] 
-    res = interpret(func, [5])
-    assert res == 6
-
-def test_dict_get():
-    def func():
-        dic = {}
-        x1 = dic.get('hi', 42)
-        dic['blah'] = 1 # XXX this triggers type determination
-        x2 = dic.get('blah', 2)
-        return x1 * 10 + x2
-    res = interpret(func, ())
-    assert res == 421
-
-def test_dict_get_empty():
-    def func():
-        # this time without writing to the dict
-        dic = {}
-        x1 = dic.get('hi', 42)
-        x2 = dic.get('blah', 2)
-        return x1 * 10 + x2
-    res = interpret(func, ())
-    assert res == 422
-
-def test_dict_setdefault():
-    def f():
-        d = {}
-        d.setdefault('a', 2)
-        return d['a']
-    res = interpret(f, ())
-    assert res == 2
-
-    def f():
-        d = {}
-        d.setdefault('a', 2)
-        x = d.setdefault('a', -3)
-        return x
-    res = interpret(f, ())
-    assert res == 2
-
-def test_dict_copy():
-    def func():
-        # XXX this does not work if we use chars, only!
-        dic = {'ab':1, 'b':2}
-        d2 = dic.copy()
-        ok = 1
-        for key in d2:
-            if dic[key] != d2[key]:
-                ok = 0
-        ok &= len(dic) == len(d2)
-        d2['c'] = 3
-        ok &= len(dic) == len(d2) - 1
-        return ok
-    res = interpret(func, ())
-    assert res == 1
-
-def test_dict_update():
-    def func():
-        dic = {'ab':1000, 'b':200}
-        d2 = {'b':30, 'cb':4}
-        dic.update(d2)
-        ok = len(dic) == 3
-        sum = ok
-        for key in dic:
-            sum += dic[key]
-        return sum
-    res = interpret(func, ())
-    assert res == 1035
-
-def test_dict_keys():
-    def func():
-        dic = {' 4':1000, ' 8':200}
-        keys = dic.keys()
-        return ord(keys[0][1]) + ord(keys[1][1]) - 2*ord('0') + len(keys)
-    res = interpret(func, ())#, view=True)
-    assert res == 14
-
-def test_dict_inst_keys():
-    class Empty:
-        pass
-    class A(Empty):
-        pass
-    def func():
-        dic0 = {Empty(): 2}
-        dic = {A(): 1, A(): 2}
-        keys = dic.keys()
-        return (isinstance(keys[1], A))*2+(isinstance(keys[0],A))
-    res = interpret(func, [])
-    assert res == 3
-
-def test_dict_inst_iterkeys():
-    class Empty:
-        pass
-    class A(Empty):
-        pass
-    def func():
-        dic0 = {Empty(): 2}
-        dic = {A(): 1, A(): 2}
-        a = 0
-        for k in dic.iterkeys():
-            a += isinstance(k, A)
-        return a
-    res = interpret(func, [])
-    assert res == 2
-
-def test_dict_values():
-    def func():
-        dic = {' 4':1000, ' 8':200}
-        values = dic.values()
-        return values[0] + values[1] + len(values)
-    res = interpret(func, ())
-    assert res == 1202
-
-def test_dict_inst_values():
-    class A:
-        pass
-    def func():
-        dic = {1: A(), 2: A()}
-        vals = dic.values()
-        return (isinstance(vals[1], A))*2+(isinstance(vals[0],A))
-    res = interpret(func, [])
-    assert res == 3
-
-def test_dict_inst_itervalues():
-    class A:
-        pass
-    def func():
-        dic = {1: A(), 2: A()}
-        a = 0
-        for v in dic.itervalues():
-            a += isinstance(v, A)
-        return a
-    res = interpret(func, [])
-    assert res == 2
-
-def test_dict_inst_items():
-    class Empty:
-        pass
-    class A:
-        pass
-    class B(Empty):
-        pass
-    def func():
-        dic0 = {Empty(): 2}
-        dic = {B(): A(), B(): A()}
-        items = dic.items()
-        b = 0
-        a = 0
-        for k, v in items:
-            b += isinstance(k, B)
-            a += isinstance(v, A) 
-        return 3*b+a
-    res = interpret(func, [])
-    assert res == 8
-
-def test_dict_inst_iteritems():
-    class Empty:
-        pass
-    class A:
-        pass
-    class B(Empty):
-        pass
-    def func():
-        dic0 = {Empty(): 2}
-        dic = {B(): A(), B(): A()}
-        b = 0
-        a = 0
-        for k, v in dic.iteritems():
-            b += isinstance(k, B)
-            a += isinstance(v, A) 
-        return 3*b+a
-    res = interpret(func, [])
-    assert res == 8
-
-def test_dict_items():
-    def func():
-        dic = {' 4':1000, ' 8':200}
-        items = dic.items()
-        res = len(items)
-        for key, value in items:
-            res += ord(key[1]) - ord('0') + value
-        return res
-    res = interpret(func, ())
-    assert res == 1214
-
-def test_dict_contains():
-    def func():
-        dic = {' 4':1000, ' 8':200}
-        return ' 4' in dic and ' 9' not in dic
-    res = interpret(func, ())
-    assert res is True
-
-def test_dict_contains_with_constant_dict():
-    dic = {'4':1000, ' 8':200}
-    def func(i):
-        return chr(i) in dic 
-    res = interpret(func, [ord('4')]) 
-    assert res is True
-    res = interpret(func, [1]) 
-    assert res is False 
-
-def dict_or_none():
-    class A:
-        pass
-    def negate(d):
-        return not d
-    def func(n):
-        a = A()
-        a.d = None
-        if n > 0:
-            a.d = {str(n): 1, "42": 2}
-            del a.d["42"]
-        return negate(a.d)
-    res = interpret(func, [10])
-    assert res is False
-    res = interpret(func, [0])
-    assert res is True
-    res = interpret(func, [42])
-    assert res is True
-
-def test_int_dict():
-    def func(a, b):
-        dic = {12: 34}
-        dic[a] = 1000
-        return dic.get(b, -123)
-    res = interpret(func, [12, 12])
-    assert res == 1000
-    res = interpret(func, [12, 13])
-    assert res == -123
-    res = interpret(func, [524, 12])
-    assert res == 34
-    res = interpret(func, [524, 524])
-    assert res == 1000
-    res = interpret(func, [524, 1036])
-    assert res == -123
-
-# ____________________________________________________________
-
 def not_really_random():
     """A random-ish generator, which also generates nice patterns from time to time.
     Could be useful to detect problems associated with specific usage patterns."""
@@ -470,283 +20,75 @@
         assert 0 <= x < 4
         yield x
 
-def test_stress():
-    from pypy.annotation.dictdef import DictKey, DictValue
-    from pypy.annotation import model as annmodel
-    dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr,
-                              DictKey(None, annmodel.SomeInteger()),
-                              DictValue(None, annmodel.SomeInteger()))
-    dictrepr.setup()
-    l_dict = rdict.ll_newdict(dictrepr.DICT)
-    referencetable = [None] * 400
-    referencelength = 0
-    value = 0
-
-    def complete_check():
-        for n, refvalue in zip(range(len(referencetable)), referencetable):
-            try:
-                gotvalue = rdict.ll_dict_getitem(l_dict, n)
-            except KeyError:
-                assert refvalue is None
-            else:
-                assert gotvalue == refvalue
-
-    for x in not_really_random():
-        n = int(x*100.0)    # 0 <= x < 400
-        op = repr(x)[-1]
-        if op <= '2' and referencetable[n] is not None:
-            rdict.ll_dict_delitem(l_dict, n)
-            referencetable[n] = None
-            referencelength -= 1
-        elif op <= '6':
-            rdict.ll_dict_setitem(l_dict, n, value)
-            if referencetable[n] is None:
-                referencelength += 1
-            referencetable[n] = value
-            value += 1
-        else:
-            try:
-                gotvalue = rdict.ll_dict_getitem(l_dict, n)
-            except KeyError:
-                assert referencetable[n] is None
-            else:
-                assert gotvalue == referencetable[n]
-        if 1.38 <= x <= 1.39:
-            complete_check()
-            print 'current dict length:', referencelength
-        assert l_dict.num_items == referencelength
-    complete_check()
-
-# ____________________________________________________________
-
-def test_opt_nullkeymarker():
-    def f():
-        d = {"hello": None}
-        d["world"] = None
-        return "hello" in d, d
-    res = interpret(f, [])
-    assert res.item0 == True
-    DICT = lltype.typeOf(res.item1).TO
-    assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None string keys
-    assert not hasattr(DICT.entries.TO.OF, 'f_valid')   # strings have a dummy
-
-def test_opt_nullvaluemarker():
-    def f(n):
-        d = {-5: "abcd"}
-        d[123] = "def"
-        return len(d[n]), d
-    res = interpret(f, [-5])
-    assert res.item0 == 4
-    DICT = lltype.typeOf(res.item1).TO
-    assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None str values
-    assert not hasattr(DICT.entries.TO.OF, 'f_valid')   # strs have a dummy
-
-def test_opt_nonullmarker():
-    class A:
-        pass
-    def f(n):
-        if n > 5:
-            a = A()
-        else:
-            a = None
-        d = {a: -5441}
-        d[A()] = n+9872
-        return d[a], d
-    res = interpret(f, [-5])
-    assert res.item0 == -5441
-    DICT = lltype.typeOf(res.item1).TO
-    assert hasattr(DICT.entries.TO.OF, 'f_everused') # can-be-None A instances
-    assert not hasattr(DICT.entries.TO.OF, 'f_valid')# with a dummy A instance
-
-    res = interpret(f, [6])
-    assert res.item0 == -5441
-
-def test_opt_nonnegint_dummy():
-    def f(n):
-        d = {n: 12}
-        d[-87] = 24
-        del d[n]
-        return len(d.copy()), d[-87], d
-    res = interpret(f, [5])
-    assert res.item0 == 1
-    assert res.item1 == 24
-    DICT = lltype.typeOf(res.item2).TO
-    assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
-    assert not hasattr(DICT.entries.TO.OF, 'f_valid')# nonneg int: dummy -1
-
-def test_opt_no_dummy():
-    def f(n):
-        d = {n: 12}
-        d[-87] = -24
-        del d[n]
-        return len(d.copy()), d[-87], d
-    res = interpret(f, [5])
-    assert res.item0 == 1
-    assert res.item1 == -24
-    DICT = lltype.typeOf(res.item2).TO
-    assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
-    assert hasattr(DICT.entries.TO.OF, 'f_valid')    # no dummy available
-
-def test_opt_multiple_identical_dicts():
-    def f(n):
-        s = "x" * n
-        d1 = {s: 12}
-        d2 = {s: 24}
-        d3 = {s: 36}
-        d1["a"] = d2[s]   # 24
-        d3[s] += d1["a"]  # 60
-        d2["bc"] = d3[s]  # 60
-        return d2["bc"], d1, d2, d3
-    res = interpret(f, [5])
-    assert res.item0 == 60
-    # all three dicts should use the same low-level type
-    assert lltype.typeOf(res.item1) == lltype.typeOf(res.item2)
-    assert lltype.typeOf(res.item1) == lltype.typeOf(res.item3)
-
-# ____________________________________________________________
-
-def test_id_instances_keys():
-    class A:
-        pass
-    class B(A):
-        pass
-    def f():
-        a = A()
-        b = B()
-        d = {}
-        d[b] = 7
-        d[a] = 3
-        return len(d) + d[a] + d[b]
-    res = interpret(f, [])
-    assert res == 12
-
-def test_captured_get():
-    get = {1:2}.get
-    def f():
-        return get(1, 3)+get(2, 4)
-    res = interpret(f, [])
-    assert res == 6
-
-    def g(h):
-        return h(1, 3)
-    def f():
-        return g(get)
-
-    res = interpret(f, [])
-    assert res == 2    
-
-def test_specific_obscure_bug():
-    class A: pass
-    class B: pass   # unrelated kinds of instances
-    def f():
-        lst = [A()]
-        res1 = A() in lst
-        d2 = {B(): None, B(): None}
-        return res1+len(d2)
-    res = interpret(f, [])
-    assert res == 2
-
-
-def test_type_erase():
-    class A(object):
-        pass
-    class B(object):
-        pass
-
-    def f():
-        return {A(): B()}, {B(): A()}
-
-    t = TranslationContext()
-    s = t.buildannotator().build_types(f, [])
-    rtyper = t.buildrtyper()
-    rtyper.specialize()
-
-    s_AB_dic = s.items[0]
-    s_BA_dic = s.items[1]
-    
-    r_AB_dic = rtyper.getrepr(s_AB_dic)
-    r_BA_dic = rtyper.getrepr(s_AB_dic)
-
-    assert r_AB_dic.lowleveltype == r_BA_dic.lowleveltype
-
-def test_tuple_dict():
-    def f(i):
-        d = {}
-        d[(1, 2)] = 4
-        d[(1, 3)] = 6
-        return d[(1, i)]
-
-    res = interpret(f, [2])
-    assert res == f(2)
-        
-def test_dict_of_dict():
-    def f(n):
-        d = {}
-        d[5] = d
-        d[6] = {}
-        return len(d[n])
-
-    res = interpret(f, [5])
-    assert res == 2
-    res = interpret(f, [6])
-    assert res == 0
-
-def test_access_in_try():
-    def f(d):
-        try:
-            return d[2]
-        except ZeroDivisionError:
-            return 42
-        return -1
-    def g(n):
-        d = {1: n, 2: 2*n}
-        return f(d)
-    res = interpret(g, [3])
-    assert res == 6
-
-def test_access_in_try_set():
-    def f(d):
-        try:
-            d[2] = 77
-        except ZeroDivisionError:
-            return 42
-        return -1
-    def g(n):
-        d = {1: n}
-        f(d)
-        return d[2]
-    res = interpret(g, [3])
-    assert res == 77
 
+class BaseTestRdict(BaseRtypingTest):
 
-
-class TestLltypeRtyping(BaseTestDictRtyping):
-    ts = "lltype"
-
-class TestOotypeRtyping(BaseTestDictRtyping):
-    ts = "ootype"
-
-    def ll_to_list(self, l):
-        return l._list[:]
-
-    def ll_to_tuple(self, t, num):
-        lst = [getattr(t, 'item%d' % i) for i in range(num)]
-        return tuple(lst)
-
-    # these tests are similar to those above, but they don't use strings
     def test_dict_creation(self):
-        def createdict(i):
-            d = {i: i+1}
-            return d[i]
+        def createdict(i): 
+            d = {'hello' : i}
+            return d['hello']
+
         res = self.interpret(createdict, [42])
-        assert res == 43
+        assert res == 42
 
-    def test_dict_getitem_setitem(self):
+    def test_dict_getitem_setitem(self): 
+        def func(i): 
+            d = {'hello' : i}
+            d['world'] = i + 1
+            return d['hello'] * d['world'] 
+        res = self.interpret(func, [6])
+        assert res == 42
+
+    def test_dict_getitem_keyerror(self): 
+        def func(i): 
+            d = {'hello' : i}
+            try:
+                return d['world']
+            except KeyError:
+                return 0 
+        res = self.interpret(func, [6])
+        assert res == 0
+
+    def test_dict_del_simple(self):
+        def func(i): 
+            d = {'hello' : i}
+            d['world'] = i + 1
+            del d['hello']
+            return len(d) 
+        res = self.interpret(func, [6])
+        assert res == 1
+
+    def test_dict_clear(self):
         def func(i):
-            d = {i: i+1}
-            d[i] = i+2
-            return d[i]
-        res = self.interpret(func, [42])
+            d = {'abc': i}
+            d['def'] = i+1
+            d.clear()
+            d['ghi'] = i+2
+            return ('abc' not in d and 'def' not in d
+                    and d['ghi'] == i+2 and len(d) == 1)
+        res = self.interpret(func, [7])
+        assert res == True
+
+    def test_empty_strings(self):
+        def func(i): 
+            d = {'' : i}
+            del d['']
+            try:
+                d['']
+                return 0
+            except KeyError:
+                pass
+            return 1
+        res = self.interpret(func, [6])
+        assert res == 1
+
+        def func(i): 
+            d = {'' : i}
+            del d['']
+            d[''] = i + 1
+            return len(d)
+        res = self.interpret(func, [6])
+        assert res == 1
 
     def test_dict_is_true(self):
         def func(i):
@@ -768,115 +110,105 @@
         assert self.interpret(func, [42, 0]) == False
         assert self.interpret(func, [42, 42]) == True
 
-    def test_delitem(self):
-        def func(x, y):
-            d = {x: x+1}
-            del d[y]
-        self.interpret(func, [42, 42]) # don't raise anything
-        self.interpret_raises(KeyError, func, [42, 0])
 
-    def test_length(self):
-        def func(num):
+    def test_dict_iteration(self):
+        def func(i, j):
             d = {}
-            for i in range(num):
-                d[i] = i+1
-            return len(d)
-        assert self.interpret(func, [0]) == 0
-        assert self.interpret(func, [2]) == 2
+            d['hello'] = i
+            d['world'] = j
+            k = 1
+            for key in d:
+                k = k * d[key]
+            return k
+        res = self.interpret(func, [6, 7])
+        assert res == 42
 
-    def test_get(self):
-        def func(x, y):
-            d = {x: x+1}
-            return d.get(x, y) + d.get(x+1, y)
-        assert self.interpret(func, [42, 13]) == 56
-    
-    def test_setdefault(self):
-        def func(x, y):
+    def test_dict_itermethods(self):
+        self._skip_oo('assert')
+        def func():
             d = {}
-            d.setdefault(x, y)
-            return d[x]
-        assert self.interpret(func, [42, 13]) == 13
-
-    def test_keys(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            return d.keys()
-        res = self.ll_to_list(self.interpret(func, [42, 13]))
-        assert res == [42, 13] or res == [13, 42]
+            d['hello'] = 6
+            d['world'] = 7
+            k1 = k2 = k3 = 1
+            for key in d.iterkeys():
+                k1 = k1 * d[key]
+            for value in d.itervalues():
+                k2 = k2 * value
+            for key, value in d.iteritems():
+                assert d[key] == value
+                k3 = k3 * value
+            return k1 + k2 + k3
+        res = self.interpret(func, [])
+        assert res == 42 + 42 + 42
 
-    def test_values(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            return d.values()
-        res = self.ll_to_list(self.interpret(func, [42, 13]))
-        assert res == [43, 14] or res == [14, 43]
-    
-    def test_items(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            return d.items()
-        res = self.ll_to_list(self.interpret(func, [42, 13]))
-        res = [self.ll_to_tuple(item, 2) for item in res]
-        assert res == [(42, 43), (13, 14)] or res == [(13, 14), (42, 43)]
+    def test_two_dicts_with_different_value_types(self):
+        def func(i):
+            d1 = {}
+            d1['hello'] = i + 1
+            d2 = {}
+            d2['world'] = d1 
+            return d2['world']['hello'] 
+        res = self.interpret(func, [5])
+        assert res == 6
 
-    def test_iteration(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            tot = 0
-            for key in  d:
-                tot += key
-            return tot
-        assert self.interpret(func, [42, 13]) == 55
+    def test_dict_get(self):
+        def func():
+            dic = {}
+            x1 = dic.get('hi', 42)
+            dic['blah'] = 1 # XXX this triggers type determination
+            x2 = dic.get('blah', 2)
+            return x1 * 10 + x2
+        res = self.interpret(func, ())
+        assert res == 421
 
-    def test_iterkeys(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            tot = 0
-            for key in  d.iterkeys():
-                tot += key
-            return tot
-        assert self.interpret(func, [42, 13]) == 55
+    def test_dict_get_empty(self):
+        def func():
+            # this time without writing to the dict
+            dic = {}
+            x1 = dic.get('hi', 42)
+            x2 = dic.get('blah', 2)
+            return x1 * 10 + x2
+        res = self.interpret(func, ())
+        assert res == 422
 
-    def test_itervalues(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            tot = 0
-            for value in  d.itervalues():
-                tot += value
-            return tot
-        assert self.interpret(func, [42, 13]) == 57
+    def test_dict_setdefault(self):
+        def f():
+            d = {}
+            d.setdefault('a', 2)
+            return d['a']
+        res = self.interpret(f, ())
+        assert res == 2
 
-    def test_iteritems(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            tot1 = 0
-            tot2 = 0
-            for key, value in  d.iteritems():
-                tot1 += key
-                tot2 += value
-            return tot1, tot2
-        res = self.ll_to_tuple(self.interpret(func, [42, 13]), 2)
-        assert res == (55, 57)
+        def f():
+            d = {}
+            d.setdefault('a', 2)
+            x = d.setdefault('a', -3)
+            return x
+        res = self.interpret(f, ())
+        assert res == 2
 
-    def test_copy(self):
-        def func(x, y):
-            d = {x: x+1, y: y+1}
-            d2 = d.copy()
-            d[x] = 0
-            d[y] = 0
-            tot1 = 0
-            tot2 = 0
-            for key, value in  d2.iteritems():
-                tot1 += key
-                tot2 += value
-            return tot1, tot2
-        res = self.ll_to_tuple(self.interpret(func, [42, 13]), 2)
-        assert res == (55, 57)
+    def test_dict_copy(self):
+        self._skip_oo('mixing char and string')
+        def func():
+            # XXX this does not work if we use chars, only!
+            dic = {'ab':1, 'b':2}
+            d2 = dic.copy()
+            ok = 1
+            for key in d2:
+                if dic[key] != d2[key]:
+                    ok = 0
+            ok &= len(dic) == len(d2)
+            d2['c'] = 3
+            ok &= len(dic) == len(d2) - 1
+            return ok
+        res = self.interpret(func, ())
+        assert res == 1
 
-    def test_update(self):
+    def test_dict_update(self):
+        self._skip_oo('mixing char and string')
         def func():
-            dic = {1:1000, 2:200}
-            d2 = {2:30, 3:4}
+            dic = {'ab':1000, 'b':200}
+            d2 = {'b':30, 'cb':4}
             dic.update(d2)
             ok = len(dic) == 3
             sum = ok
@@ -886,12 +218,540 @@
         res = self.interpret(func, ())
         assert res == 1035
 
-    def test_clear(self):
+    def test_dict_keys(self):
         def func():
-            dic = {1: 2, 3:4}
-            dic.clear()
-            return len(dic)
-        assert self.interpret(func, ()) == 0
+            dic = {' 4':1000, ' 8':200}
+            keys = dic.keys()
+            return ord(keys[0][1]) + ord(keys[1][1]) - 2*ord('0') + len(keys)
+        res = self.interpret(func, ())#, view=True)
+        assert res == 14
+
+    def test_dict_inst_keys(self):
+        self._skip_oo('instances as keys')
+        class Empty:
+            pass
+        class A(Empty):
+            pass
+        def func():
+            dic0 = {Empty(): 2}
+            dic = {A(): 1, A(): 2}
+            keys = dic.keys()
+            return (isinstance(keys[1], A))*2+(isinstance(keys[0],A))
+        res = self.interpret(func, [])
+        assert res == 3
+
+    def test_dict_inst_iterkeys(self):
+        self._skip_oo('instances as keys')
+        class Empty:
+            pass
+        class A(Empty):
+            pass
+        def func():
+            dic0 = {Empty(): 2}
+            dic = {A(): 1, A(): 2}
+            a = 0
+            for k in dic.iterkeys():
+                a += isinstance(k, A)
+            return a
+        res = self.interpret(func, [])
+        assert res == 2
+
+    def test_dict_values(self):
+        def func():
+            dic = {' 4':1000, ' 8':200}
+            values = dic.values()
+            return values[0] + values[1] + len(values)
+        res = self.interpret(func, ())
+        assert res == 1202
+
+    def test_dict_inst_values(self):
+        self._skip_oo('instances as values')
+        class A:
+            pass
+        def func():
+            dic = {1: A(), 2: A()}
+            vals = dic.values()
+            return (isinstance(vals[1], A))*2+(isinstance(vals[0],A))
+        res = self.interpret(func, [])
+        assert res == 3
+
+    def test_dict_inst_itervalues(self):
+        self._skip_oo('instances as values')
+        class A:
+            pass
+        def func():
+            dic = {1: A(), 2: A()}
+            a = 0
+            for v in dic.itervalues():
+                a += isinstance(v, A)
+            return a
+        res = self.interpret(func, [])
+        assert res == 2
+
+    def test_dict_inst_items(self):
+        self._skip_oo('instances as keys')
+        class Empty:
+            pass
+        class A:
+            pass
+        class B(Empty):
+            pass
+        def func():
+            dic0 = {Empty(): 2}
+            dic = {B(): A(), B(): A()}
+            items = dic.items()
+            b = 0
+            a = 0
+            for k, v in items:
+                b += isinstance(k, B)
+                a += isinstance(v, A) 
+            return 3*b+a
+        res = self.interpret(func, [])
+        assert res == 8
+
+    def test_dict_inst_iteritems(self):
+        self._skip_oo('instances as values')
+        class Empty:
+            pass
+        class A:
+            pass
+        class B(Empty):
+            pass
+        def func():
+            dic0 = {Empty(): 2}
+            dic = {B(): A(), B(): A()}
+            b = 0
+            a = 0
+            for k, v in dic.iteritems():
+                b += isinstance(k, B)
+                a += isinstance(v, A) 
+            return 3*b+a
+        res = self.interpret(func, [])
+        assert res == 8
+
+    def test_dict_items(self):
+        def func():
+            dic = {' 4':1000, ' 8':200}
+            items = dic.items()
+            res = len(items)
+            for key, value in items:
+                res += ord(key[1]) - ord('0') + value
+            return res
+        res = self.interpret(func, ())
+        assert res == 1214
+
+    def test_dict_contains(self):
+        def func():
+            dic = {' 4':1000, ' 8':200}
+            return ' 4' in dic and ' 9' not in dic
+        res = self.interpret(func, ())
+        assert res is True
+
+    def test_dict_contains_with_constant_dict(self):
+        self._skip_oo('mixing char and string')
+        dic = {'4':1000, ' 8':200}
+        def func(i):
+            return chr(i) in dic 
+        res = self.interpret(func, [ord('4')]) 
+        assert res is True
+        res = self.interpret(func, [1]) 
+        assert res is False 
+
+    def test_dict_or_none(self):
+        class A:
+            pass
+        def negate(d):
+            return not d
+        def func(n):
+            a = A()
+            a.d = None
+            if n > 0:
+                a.d = {str(n): 1, "42": 2}
+                del a.d["42"]
+            return negate(a.d)
+        res = self.interpret(func, [10])
+        assert res is False
+        res = self.interpret(func, [0])
+        assert res is True
+        res = self.interpret(func, [42])
+        assert res is True
+
+    def test_int_dict(self):
+        def func(a, b):
+            dic = {12: 34}
+            dic[a] = 1000
+            return dic.get(b, -123)
+        res = self.interpret(func, [12, 12])
+        assert res == 1000
+        res = self.interpret(func, [12, 13])
+        assert res == -123
+        res = self.interpret(func, [524, 12])
+        assert res == 34
+        res = self.interpret(func, [524, 524])
+        assert res == 1000
+        res = self.interpret(func, [524, 1036])
+        assert res == -123
+
+
+
+    def test_id_instances_keys(self):
+        class A:
+            pass
+        class B(A):
+            pass
+        def f():
+            a = A()
+            b = B()
+            d = {}
+            d[b] = 7
+            d[a] = 3
+            return len(d) + d[a] + d[b]
+        res = self.interpret(f, [])
+        assert res == 12
+
+    def test_captured_get(self):
+        get = {1:2}.get
+        def f():
+            return get(1, 3)+get(2, 4)
+        res = self.interpret(f, [])
+        assert res == 6
+
+        def g(h):
+            return h(1, 3)
+        def f():
+            return g(get)
+
+        res = self.interpret(f, [])
+        assert res == 2    
+
+    def test_specific_obscure_bug(self):
+        class A: pass
+        class B: pass   # unrelated kinds of instances
+        def f():
+            lst = [A()]
+            res1 = A() in lst
+            d2 = {B(): None, B(): None}
+            return res1+len(d2)
+        res = self.interpret(f, [])
+        assert res == 2
+
+
+    def test_type_erase(self):
+        class A(object):
+            pass
+        class B(object):
+            pass
+
+        def f():
+            return {A(): B()}, {B(): A()}
+
+        t = TranslationContext()
+        s = t.buildannotator().build_types(f, [])
+        rtyper = t.buildrtyper()
+        rtyper.specialize()
+
+        s_AB_dic = s.items[0]
+        s_BA_dic = s.items[1]
+
+        r_AB_dic = rtyper.getrepr(s_AB_dic)
+        r_BA_dic = rtyper.getrepr(s_AB_dic)
+
+        assert r_AB_dic.lowleveltype == r_BA_dic.lowleveltype
+
+    def test_tuple_dict(self):
+        def f(i):
+            d = {}
+            d[(1, 2)] = 4
+            d[(1, 3)] = 6
+            return d[(1, i)]
+
+        res = self.interpret(f, [2])
+        assert res == f(2)
+
+    def test_dict_of_dict(self):
+        self._skip_oo('dict of dict')
+        def f(n):
+            d = {}
+            d[5] = d
+            d[6] = {}
+            return len(d[n])
+
+        res = self.interpret(f, [5])
+        assert res == 2
+        res = self.interpret(f, [6])
+        assert res == 0
+
+    def test_access_in_try(self):
+        def f(d):
+            try:
+                return d[2]
+            except ZeroDivisionError:
+                return 42
+            return -1
+        def g(n):
+            d = {1: n, 2: 2*n}
+            return f(d)
+        res = self.interpret(g, [3])
+        assert res == 6
+
+    def test_access_in_try_set(self):
+        def f(d):
+            try:
+                d[2] = 77
+            except ZeroDivisionError:
+                return 42
+            return -1
+        def g(n):
+            d = {1: n}
+            f(d)
+            return d[2]
+        res = self.interpret(g, [3])
+        assert res == 77
+
+
+class TestLLtype(BaseTestRdict, LLRtypeMixin):
+    def test_dict_but_not_with_char_keys(self):
+        def func(i):
+            d = {'h': i}
+            try:
+                return d['hello']
+            except KeyError:
+                return 0
+        res = self.interpret(func, [6])
+        assert res == 0
+
+    def test_deleted_entry_reusage_with_colliding_hashes(self): 
+        def lowlevelhash(value): 
+            p = lltype.malloc(rstr.STR, len(value))
+            for i in range(len(value)):
+                p.chars[i] = value[i]
+            return rstr.LLHelpers.ll_strhash(p) 
+
+        def func(c1, c2): 
+            c1 = chr(c1) 
+            c2 = chr(c2) 
+            d = {}
+            d[c1] = 1
+            d[c2] = 2 
+            del d[c1]
+            return d[c2]
+
+        char_by_hash = {}
+        base = rdict.DICT_INITSIZE
+        for y in range(0, 256):
+            y = chr(y)
+            y_hash = lowlevelhash(y) % base 
+            char_by_hash.setdefault(y_hash, []).append(y)
+
+        x, y = char_by_hash[0][:2]   # find a collision
+
+        res = self.interpret(func, [ord(x), ord(y)])
+        assert res == 2
+
+        def func2(c1, c2): 
+            c1 = chr(c1) 
+            c2 = chr(c2) 
+            d = {}
+            d[c1] = 1
+            d[c2] = 2 
+            del d[c1]
+            d[c1] = 3
+            return d 
+
+        res = self.interpret(func2, [ord(x), ord(y)])
+        for i in range(len(res.entries)): 
+            assert not (res.entries[i].everused() and not res.entries[i].valid())
+
+        def func3(c0, c1, c2, c3, c4, c5, c6, c7):
+            d = {}
+            c0 = chr(c0) ; d[c0] = 1; del d[c0]
+            c1 = chr(c1) ; d[c1] = 1; del d[c1]
+            c2 = chr(c2) ; d[c2] = 1; del d[c2]
+            c3 = chr(c3) ; d[c3] = 1; del d[c3]
+            c4 = chr(c4) ; d[c4] = 1; del d[c4]
+            c5 = chr(c5) ; d[c5] = 1; del d[c5]
+            c6 = chr(c6) ; d[c6] = 1; del d[c6]
+            c7 = chr(c7) ; d[c7] = 1; del d[c7]
+            return d
+
+        if rdict.DICT_INITSIZE != 8: 
+            py.test.skip("make dict tests more indepdent from initsize")
+        res = self.interpret(func3, [ord(char_by_hash[i][0]) 
+                                   for i in range(rdict.DICT_INITSIZE)])
+        count_frees = 0
+        for i in range(len(res.entries)):
+            if not res.entries[i].everused():
+                count_frees += 1
+        assert count_frees >= 3
+
+    def test_dict_resize(self):
+        def func(want_empty):
+            d = {}
+            for i in range(rdict.DICT_INITSIZE):
+                d[chr(ord('a') + i)] = i
+            if want_empty:
+                for i in range(rdict.DICT_INITSIZE):
+                    del d[chr(ord('a') + i)]
+            return d
+        res = self.interpret(func, [0])
+        assert len(res.entries) > rdict.DICT_INITSIZE 
+        res = self.interpret(func, [1])
+        assert len(res.entries) == rdict.DICT_INITSIZE 
+
+    def test_dict_valid_resize(self):
+        # see if we find our keys after resize
+        def func():
+            d = {}
+            # fill it up
+            for i in range(10):
+                d[str(i)] = 0
+            # delete again
+            for i in range(10):
+                del d[str(i)]
+            res = 0
+        # if it does not crash, we are fine. It crashes if you forget the hash field.
+        self.interpret(func, [])
+
+    # ____________________________________________________________
+
+    def test_stress(self):
+        from pypy.annotation.dictdef import DictKey, DictValue
+        from pypy.annotation import model as annmodel
+        dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr,
+                                  DictKey(None, annmodel.SomeInteger()),
+                                  DictValue(None, annmodel.SomeInteger()))
+        dictrepr.setup()
+        l_dict = rdict.ll_newdict(dictrepr.DICT)
+        referencetable = [None] * 400
+        referencelength = 0
+        value = 0
+
+        def complete_check():
+            for n, refvalue in zip(range(len(referencetable)), referencetable):
+                try:
+                    gotvalue = rdict.ll_dict_getitem(l_dict, n)
+                except KeyError:
+                    assert refvalue is None
+                else:
+                    assert gotvalue == refvalue
+
+        for x in not_really_random():
+            n = int(x*100.0)    # 0 <= x < 400
+            op = repr(x)[-1]
+            if op <= '2' and referencetable[n] is not None:
+                rdict.ll_dict_delitem(l_dict, n)
+                referencetable[n] = None
+                referencelength -= 1
+            elif op <= '6':
+                rdict.ll_dict_setitem(l_dict, n, value)
+                if referencetable[n] is None:
+                    referencelength += 1
+                referencetable[n] = value
+                value += 1
+            else:
+                try:
+                    gotvalue = rdict.ll_dict_getitem(l_dict, n)
+                except KeyError:
+                    assert referencetable[n] is None
+                else:
+                    assert gotvalue == referencetable[n]
+            if 1.38 <= x <= 1.39:
+                complete_check()
+                print 'current dict length:', referencelength
+            assert l_dict.num_items == referencelength
+        complete_check()
+
+    # ____________________________________________________________
+
+    def test_opt_nullkeymarker(self):
+        def f():
+            d = {"hello": None}
+            d["world"] = None
+            return "hello" in d, d
+        res = self.interpret(f, [])
+        assert res.item0 == True
+        DICT = lltype.typeOf(res.item1).TO
+        assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None string keys
+        assert not hasattr(DICT.entries.TO.OF, 'f_valid')   # strings have a dummy
+
+    def test_opt_nullvaluemarker(self):
+        def f(n):
+            d = {-5: "abcd"}
+            d[123] = "def"
+            return len(d[n]), d
+        res = self.interpret(f, [-5])
+        assert res.item0 == 4
+        DICT = lltype.typeOf(res.item1).TO
+        assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None str values
+        assert not hasattr(DICT.entries.TO.OF, 'f_valid')   # strs have a dummy
+
+    def test_opt_nonullmarker(self):
+        class A:
+            pass
+        def f(n):
+            if n > 5:
+                a = A()
+            else:
+                a = None
+            d = {a: -5441}
+            d[A()] = n+9872
+            return d[a], d
+        res = self.interpret(f, [-5])
+        assert res.item0 == -5441
+        DICT = lltype.typeOf(res.item1).TO
+        assert hasattr(DICT.entries.TO.OF, 'f_everused') # can-be-None A instances
+        assert not hasattr(DICT.entries.TO.OF, 'f_valid')# with a dummy A instance
+
+        res = self.interpret(f, [6])
+        assert res.item0 == -5441
+
+    def test_opt_nonnegint_dummy(self):
+        def f(n):
+            d = {n: 12}
+            d[-87] = 24
+            del d[n]
+            return len(d.copy()), d[-87], d
+        res = self.interpret(f, [5])
+        assert res.item0 == 1
+        assert res.item1 == 24
+        DICT = lltype.typeOf(res.item2).TO
+        assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
+        assert not hasattr(DICT.entries.TO.OF, 'f_valid')# nonneg int: dummy -1
+
+    def test_opt_no_dummy(self):
+        def f(n):
+            d = {n: 12}
+            d[-87] = -24
+            del d[n]
+            return len(d.copy()), d[-87], d
+        res = self.interpret(f, [5])
+        assert res.item0 == 1
+        assert res.item1 == -24
+        DICT = lltype.typeOf(res.item2).TO
+        assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
+        assert hasattr(DICT.entries.TO.OF, 'f_valid')    # no dummy available
+
+    def test_opt_multiple_identical_dicts(self):
+        def f(n):
+            s = "x" * n
+            d1 = {s: 12}
+            d2 = {s: 24}
+            d3 = {s: 36}
+            d1["a"] = d2[s]   # 24
+            d3[s] += d1["a"]  # 60
+            d2["bc"] = d3[s]  # 60
+            return d2["bc"], d1, d2, d3
+        res = self.interpret(f, [5])
+        assert res.item0 == 60
+        # all three dicts should use the same low-level type
+        assert lltype.typeOf(res.item1) == lltype.typeOf(res.item2)
+        assert lltype.typeOf(res.item1) == lltype.typeOf(res.item3)
+
+    # ____________________________________________________________
+
+
+
+class TestOOtype(BaseTestRdict, OORtypeMixin):
 
     def test_recursive(self):
         def func(i):
@@ -901,3 +761,6 @@
         res = self.interpret(func, [5])
         assert res.ll_get(5) is res
 
+
+
+



More information about the Pypy-commit mailing list