Author: Maciej Fijalkowski <[email protected]>
Branch: rordereddict
Changeset: r67676:bdb7bae76f74
Date: 2013-10-29 11:40 +0200
http://bitbucket.org/pypy/pypy/changeset/bdb7bae76f74/
Log: finish porting
diff --git a/rpython/annotator/builtin.py b/rpython/annotator/builtin.py
--- a/rpython/annotator/builtin.py
+++ b/rpython/annotator/builtin.py
@@ -300,6 +300,10 @@
dictdef.dictkey.update_rdict_annotations(s_eqfn, s_hashfn)
return SomeDict(dictdef)
+def robjmodel_r_ordereddict(s_eqfn, s_hashfn):
+ dictdef = getbookkeeper().getdictdef(is_r_dict=True)
+ dictdef.dictkey.update_rdict_annotations(s_eqfn, s_hashfn)
+ return SomeOrderedDict(dictdef)
def robjmodel_hlinvoke(s_repr, s_llcallable, *args_s):
from rpython.rtyper import rmodel
@@ -359,6 +363,7 @@
BUILTIN_ANALYZERS[rpython.rlib.rarithmetic.longlongmask] = rarith_longlongmask
BUILTIN_ANALYZERS[rpython.rlib.objectmodel.instantiate] = robjmodel_instantiate
BUILTIN_ANALYZERS[rpython.rlib.objectmodel.r_dict] = robjmodel_r_dict
+BUILTIN_ANALYZERS[rpython.rlib.objectmodel.r_ordereddict] =
robjmodel_r_ordereddict
BUILTIN_ANALYZERS[OrderedDict] = lambda :
SomeOrderedDict(getbookkeeper().getdictdef())
BUILTIN_ANALYZERS[rpython.rlib.objectmodel.hlinvoke] = robjmodel_hlinvoke
BUILTIN_ANALYZERS[rpython.rlib.objectmodel.keepalive_until_here] =
robjmodel_keepalive_until_here
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -628,8 +628,11 @@
The functions key_eq() and key_hash() are used by the key comparison
algorithm."""
+ def _newdict(self):
+ return {}
+
def __init__(self, key_eq, key_hash, force_non_null=False):
- self._dict = {}
+ self._dict = self._newdict()
self.key_eq = key_eq
self.key_hash = key_hash
self.force_non_null = force_non_null
@@ -664,7 +667,7 @@
return dk.key, value
def copy(self):
- result = r_dict(self.key_eq, self.key_hash)
+ result = self.__class__(self.key_eq, self.key_hash)
result.update(self)
return result
@@ -700,6 +703,11 @@
def __hash__(self):
raise TypeError("cannot hash r_dict instances")
+class r_ordereddict(r_dict):
+ def _newdict(self):
+ from collections import OrderedDict
+
+ return OrderedDict()
class _r_dictkey(object):
__slots__ = ['dic', 'key', 'hash']
@@ -735,7 +743,7 @@
Function and staticmethod objects are duplicated, which means
that annotation will not consider them as identical to another
copy in another unrelated class.
-
+
By default, "special" methods and class attributes, with a name
like "__xxx__", are not copied unless they are "__init__" or
"__del__". The list can be changed with the optional second
diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py
--- a/rpython/rtyper/rbuiltin.py
+++ b/rpython/rtyper/rbuiltin.py
@@ -734,13 +734,23 @@
hop.exception_cannot_occur()
r_dict = hop.r_result
cDICT = hop.inputconst(lltype.Void, r_dict.DICT)
- return hop.gendirectcall(ll_newdict, cDICT)
+ v_result = hop.gendirectcall(ll_newdict, cDICT)
+ v_eqfn = hop.inputarg(r_dict.r_rdict_eqfn, arg=0)
+ v_hashfn = hop.inputarg(r_dict.r_rdict_hashfn, arg=1)
+ if r_dict.r_rdict_eqfn.lowleveltype != lltype.Void:
+ cname = hop.inputconst(lltype.Void, 'fnkeyeq')
+ hop.genop('setfield', [v_result, cname, v_eqfn])
+ if r_dict.r_rdict_hashfn.lowleveltype != lltype.Void:
+ cname = hop.inputconst(lltype.Void, 'fnkeyhash')
+ hop.genop('setfield', [v_result, cname, v_hashfn])
+ return v_result
BUILTIN_TYPER[objectmodel.instantiate] = rtype_instantiate
BUILTIN_TYPER[isinstance] = rtype_builtin_isinstance
BUILTIN_TYPER[hasattr] = rtype_builtin_hasattr
BUILTIN_TYPER[objectmodel.r_dict] = rtype_r_dict
BUILTIN_TYPER[OrderedDict] = rtype_ordered_dict
+BUILTIN_TYPER[objectmodel.r_ordereddict] = rtype_ordered_dict
# _________________________________________________________________
# weakrefs
diff --git a/rpython/rtyper/test/test_rdict.py
b/rpython/rtyper/test/test_rdict.py
--- a/rpython/rtyper/test/test_rdict.py
+++ b/rpython/rtyper/test/test_rdict.py
@@ -594,42 +594,6 @@
res = self.interpret(g, [3])
assert res == 77
- def test_r_dict(self):
- class FooError(Exception):
- pass
- def myeq(n, m):
- return n == m
- def myhash(n):
- if n < 0:
- raise FooError
- return -n
- def f(n):
- d = r_dict(myeq, myhash)
- for i in range(10):
- d[i] = i*i
- try:
- value1 = d[n]
- except FooError:
- value1 = 99
- try:
- value2 = n in d
- except FooError:
- value2 = 99
- try:
- value3 = d[-n]
- except FooError:
- value3 = 99
- try:
- value4 = (-n) in d
- except FooError:
- value4 = 99
- return (value1 * 1000000 +
- value2 * 10000 +
- value3 * 100 +
- value4)
- res = self.interpret(f, [5])
- assert res == 25019999
-
def test_resize_during_iteration(self):
def func():
d = self.newdict()
@@ -706,30 +670,6 @@
res = self.interpret(func, [])
assert res in [5263, 6352]
- def test_dict_popitem_hash(self):
- def deq(n, m):
- return n == m
- def dhash(n):
- return ~n
- def func():
- d = r_dict(deq, dhash)
- d[5] = 2
- d[6] = 3
- k1, v1 = d.popitem()
- assert len(d) == 1
- k2, v2 = d.popitem()
- try:
- d.popitem()
- except KeyError:
- pass
- else:
- assert 0, "should have raised KeyError"
- assert len(d) == 0
- return k1*1000 + v1*100 + k2*10 + v2
-
- res = self.interpret(func, [])
- assert res in [5263, 6352]
-
def test_dict_pop(self):
def f(n, default):
d = self.newdict()
@@ -777,74 +717,10 @@
res = self.interpret(func, [6])
assert res == 0
- def test_deleted_entry_reusage_with_colliding_hashes(self):
- def lowlevelhash(value):
- p = rstr.mallocstr(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 = self.newdict()
- 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.everused(i) and not res.entries.valid(i))
-
- 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.everused(i):
- count_frees += 1
- assert count_frees >= 3
-
def test_dict_valid_resize(self):
# see if we find our keys after resize
def func():
- d = {}
+ d = self.newdict()
# fill it up
for i in range(10):
d[str(i)] = 0
@@ -939,25 +815,6 @@
assert self.interpret(func, [5]) == 123
assert self.interpret(func, [42]) == 321
- def test_nonnull_hint(self):
- def eq(a, b):
- return a == b
- def rhash(a):
- return 3
-
- def func(i):
- d = r_dict(eq, rhash, force_non_null=True)
- if not i:
- d[None] = i
- else:
- d[str(i)] = i
- return "12" in d, d
-
- llres = self.interpret(func, [12])
- assert llres.item0 == 1
- DICT = lltype.typeOf(llres.item1)
- assert sorted(DICT.TO.entries.TO.OF._flds) == ['f_hash', 'key',
'value']
-
def test_memoryerror_should_not_insert(self):
# This shows a misbehaviour that also exists in CPython 2.7, but not
# any more in CPython 3.3. The behaviour is that even if a dict
@@ -1160,6 +1017,149 @@
assert lltype.typeOf(res.item1) == lltype.typeOf(res.item2)
assert lltype.typeOf(res.item1) == lltype.typeOf(res.item3)
+ def test_r_dict(self):
+ class FooError(Exception):
+ pass
+ def myeq(n, m):
+ return n == m
+ def myhash(n):
+ if n < 0:
+ raise FooError
+ return -n
+ def f(n):
+ d = r_dict(myeq, myhash)
+ for i in range(10):
+ d[i] = i*i
+ try:
+ value1 = d[n]
+ except FooError:
+ value1 = 99
+ try:
+ value2 = n in d
+ except FooError:
+ value2 = 99
+ try:
+ value3 = d[-n]
+ except FooError:
+ value3 = 99
+ try:
+ value4 = (-n) in d
+ except FooError:
+ value4 = 99
+ return (value1 * 1000000 +
+ value2 * 10000 +
+ value3 * 100 +
+ value4)
+ res = self.interpret(f, [5])
+ assert res == 25019999
+
+ def test_dict_popitem_hash(self):
+ def deq(n, m):
+ return n == m
+ def dhash(n):
+ return ~n
+ def func():
+ d = r_dict(deq, dhash)
+ d[5] = 2
+ d[6] = 3
+ k1, v1 = d.popitem()
+ assert len(d) == 1
+ k2, v2 = d.popitem()
+ try:
+ d.popitem()
+ except KeyError:
+ pass
+ else:
+ assert 0, "should have raised KeyError"
+ assert len(d) == 0
+ return k1*1000 + v1*100 + k2*10 + v2
+
+ res = self.interpret(func, [])
+ assert res in [5263, 6352]
+
+ def test_nonnull_hint(self):
+ def eq(a, b):
+ return a == b
+ def rhash(a):
+ return 3
+
+ def func(i):
+ d = r_dict(eq, rhash, force_non_null=True)
+ if not i:
+ d[None] = i
+ else:
+ d[str(i)] = i
+ return "12" in d, d
+
+ llres = self.interpret(func, [12])
+ assert llres.item0 == 1
+ DICT = lltype.typeOf(llres.item1)
+ assert sorted(DICT.TO.entries.TO.OF._flds) == ['f_hash', 'key',
'value']
+
+ def test_deleted_entry_reusage_with_colliding_hashes(self):
+ def lowlevelhash(value):
+ p = rstr.mallocstr(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 = self.newdict()
+ 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 = self.newdict()
+ 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.everused(i) and not res.entries.valid(i))
+
+ def func3(c0, c1, c2, c3, c4, c5, c6, c7):
+ d = self.newdict()
+ 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.everused(i):
+ count_frees += 1
+ assert count_frees >= 3
+
class TestStress:
def test_stress(self):
diff --git a/rpython/rtyper/test/test_rordereddict.py
b/rpython/rtyper/test/test_rordereddict.py
--- a/rpython/rtyper/test/test_rordereddict.py
+++ b/rpython/rtyper/test/test_rordereddict.py
@@ -6,6 +6,7 @@
from rpython.rlib.rarithmetic import intmask
from rpython.rtyper.annlowlevel import llstr, hlstr
from rpython.rtyper.test.test_rdict import BaseTestRDict
+from rpython.rlib import objectmodel
def get_indexes(ll_d):
@@ -275,3 +276,64 @@
def test_memoryerror_should_not_insert(self):
py.test.skip("I don't want to edit this file on two branches")
+
+
+ def test_r_dict(self):
+ class FooError(Exception):
+ pass
+ def myeq(n, m):
+ return n == m
+ def myhash(n):
+ if n < 0:
+ raise FooError
+ return -n
+ def f(n):
+ d = objectmodel.r_ordereddict(myeq, myhash)
+ for i in range(10):
+ d[i] = i*i
+ try:
+ value1 = d[n]
+ except FooError:
+ value1 = 99
+ try:
+ value2 = n in d
+ except FooError:
+ value2 = 99
+ try:
+ value3 = d[-n]
+ except FooError:
+ value3 = 99
+ try:
+ value4 = (-n) in d
+ except FooError:
+ value4 = 99
+ return (value1 * 1000000 +
+ value2 * 10000 +
+ value3 * 100 +
+ value4)
+ res = self.interpret(f, [5])
+ assert res == 25019999
+
+ def test_dict_popitem_hash(self):
+ def deq(n, m):
+ return n == m
+ def dhash(n):
+ return ~n
+ def func():
+ d = objectmodel.r_ordereddict(deq, dhash)
+ d[5] = 2
+ d[6] = 3
+ k1, v1 = d.popitem()
+ assert len(d) == 1
+ k2, v2 = d.popitem()
+ try:
+ d.popitem()
+ except KeyError:
+ pass
+ else:
+ assert 0, "should have raised KeyError"
+ assert len(d) == 0
+ return k1*1000 + v1*100 + k2*10 + v2
+
+ res = self.interpret(func, [])
+ assert res in [5263, 6352]
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit