[pypy-svn] r36136 - in pypy/dist/pypy/objspace/std: . test
cfbolz at codespeak.net
cfbolz at codespeak.net
Thu Jan 4 00:47:54 CET 2007
Author: cfbolz
Date: Thu Jan 4 00:47:53 2007
New Revision: 36136
Modified:
pypy/dist/pypy/objspace/std/dictmultiobject.py
pypy/dist/pypy/objspace/std/test/test_dictmultiobject.py
Log:
implement the iter* methods off most multidict implementations in a more
sensible way: before, most dict implementations first converted themselves to
an rdict, then created the iterator just to throw the converted implementation
away (which makes the iter* methods O(n) instead of O(1)). Now there is a way
better interface that for iteration and most of the dict implementations do
something sensible for iteration.
All tests pass and pypy-withmultidict-c seems to translate on my machine (no, I
am not making the mistake of checkin in untested code again on the same day :-)
).
Modified: pypy/dist/pypy/objspace/std/dictmultiobject.py
==============================================================================
--- pypy/dist/pypy/objspace/std/dictmultiobject.py (original)
+++ pypy/dist/pypy/objspace/std/dictmultiobject.py Thu Jan 4 00:47:53 2007
@@ -16,7 +16,10 @@
# XXX there are much more types
return (space.is_w(w_lookup_type, space.w_NoneType) or
- space.is_w(w_lookup_type, space.w_int))
+ space.is_w(w_lookup_type, space.w_int) or
+ space.is_w(w_lookup_type, space.w_bool) or
+ space.is_w(w_lookup_type, space.w_float)
+ )
# DictImplementation lattice
@@ -57,11 +60,32 @@
def keys(self):
- return [w_k for w_k in self.iterkeys()]
+ iterator = self.iterkeys()
+ result = []
+ while 1:
+ w_key = iterator.next()
+ if w_key is not None:
+ result.append(w_key)
+ else:
+ return result
def values(self):
- return [w_v for w_v in self.itervalues()]
+ iterator = self.itervalues()
+ result = []
+ while 1:
+ w_value = iterator.next()
+ if w_value is not None:
+ result.append(w_value)
+ else:
+ return result
def items(self):
- return [(w_key, w_value) or w_key, w_value in self.iteritems()]
+ iterator = self.iteritems()
+ result = []
+ while 1:
+ w_item = iterator.next()
+ if w_item is not None:
+ result.append(w_item)
+ else:
+ return result
# the following method only makes sense when the option to use the
# CALL_LIKELY_BUILTIN opcode is set. Otherwise it won't even be seen
@@ -71,6 +95,40 @@
return self.get(w_key)
+# Iterator Implementation base classes
+
+class IteratorImplementation(object):
+ def __init__(self, space, implementation):
+ self.space = space
+ self.dictimplementation = implementation
+ self.len = implementation.length()
+ self.pos = 0
+
+ def next(self):
+ if self.dictimplementation is None:
+ return None
+ if self.len != self.dictimplementation.length():
+ self.len = -1 # Make this error state sticky
+ raise OperationError(self.space.w_RuntimeError,
+ self.space.wrap("dictionary changed size during iteration"))
+ # look for the next entry
+ w_result = self.next_entry()
+ if w_result is not None:
+ self.pos += 1
+ return w_result
+ # no more entries
+ self.dictimplementation = None
+ return None
+
+ def length(self):
+ if self.dictimplementation is not None:
+ return self.len - self.pos
+ return 0
+
+
+
+# concrete subclasses of the above
+
class EmptyDictImplementation(DictImplementation):
def __init__(self, space):
self.space = space
@@ -94,11 +152,11 @@
return 0
def iteritems(self):
- return RDictImplementation(self.space).iteritems()
+ return EmptyIteratorImplementation(self.space, self)
def iterkeys(self):
- return RDictImplementation(self.space).iterkeys()
+ return EmptyIteratorImplementation(self.space, self)
def itervalues(self):
- return RDictImplementation(self.space).itervalues()
+ return EmptyIteratorImplementation(self.space, self)
def keys(self):
return []
@@ -107,6 +165,11 @@
def items(self):
return []
+
+class EmptyIteratorImplementation(IteratorImplementation):
+ def next_entry(self):
+ return None
+
class Entry(object):
def __init__(self):
self.hash = 0
@@ -191,7 +254,9 @@
def values(self):
return [self.entries[i].w_value for i in range(self.valid)]
def items(self):
- return [(e.w_key, e.w_value) for e in [self.entries[i] for i in range(self.valid)]]
+ return [self.space.newtuple([e.w_key, e.w_value])
+ for e in [self.entries[i] for i in range(self.valid)]]
+
class StrEntry(object):
def __init__(self):
@@ -311,8 +376,9 @@
def values(self):
return [self.entries[i].w_value for i in range(self.valid)]
def items(self):
- return [(self.space.wrap(e.key), e.w_value)
- for e in [self.entries[i] for i in range(self.valid)]]
+ return [self.space.newtuple([self.space.wrap(e.key), e.w_value])
+ for e in [self.entries[i] for i in range(self.valid)]]
+
class StrDictImplementation(DictImplementation):
def __init__(self, space):
@@ -358,13 +424,13 @@
return self._as_rdict().get(w_lookup)
def iteritems(self):
- return self._as_rdict().iteritems()
+ return StrItemIteratorImplementation(self.space, self)
def iterkeys(self):
- return self._as_rdict().iterkeys()
+ return StrKeyIteratorImplementation(self.space, self)
def itervalues(self):
- return self._as_rdict().itervalues()
+ return StrValueIteratorImplementation(self.space, self)
def keys(self):
space = self.space
@@ -375,7 +441,8 @@
def items(self):
space = self.space
- return [(space.wrap(key), w_value) for (key, w_value) in self.content.iteritems()]
+ return [space.newtuple([space.wrap(key), w_value])
+ for (key, w_value) in self.content.iteritems()]
def _as_rdict(self):
@@ -384,6 +451,46 @@
newimpl.setitem(self.space.wrap(k), w_v)
return newimpl
+
+# the following are very close copies of the base classes above
+
+class StrKeyIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.content.iterkeys()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for key in self.iterator:
+ return self.space.wrap(key)
+ else:
+ return None
+
+class StrValueIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.content.itervalues()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for w_value in self.iterator:
+ return w_value
+ else:
+ return None
+
+class StrItemIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.content.iteritems()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for key, w_value in self.iterator:
+ return self.space.newtuple([self.space.wrap(key), w_value])
+ else:
+ return None
+
+
class WaryDictImplementation(StrDictImplementation):
def __init__(self, space):
StrDictImplementation.__init__(self, space)
@@ -440,18 +547,57 @@
return self.content.get(w_lookup, None)
def iteritems(self):
- return self.content.iteritems()
+ return RDictItemIteratorImplementation(self.space, self)
def iterkeys(self):
- return self.content.iterkeys()
+ return RDictKeyIteratorImplementation(self.space, self)
def itervalues(self):
- return self.content.itervalues()
+ return RDictValueIteratorImplementation(self.space, self)
def keys(self):
return self.content.keys()
def values(self):
return self.content.values()
def items(self):
- return self.content.items()
+ return [self.space.newtuple([w_key, w_val])
+ for w_key, w_val in self.content.iteritems()]
+
+
+class RDictKeyIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.content.iterkeys()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for w_key in self.iterator:
+ return w_key
+ else:
+ return None
+
+class RDictValueIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.content.itervalues()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for w_value in self.iterator:
+ return w_value
+ else:
+ return None
+
+class RDictItemIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.content.iteritems()
+
+ def next_entry(self):
+ # note that this 'for' loop only runs once, at most
+ for w_key, w_value in self.iterator:
+ return self.space.newtuple([w_key, w_value])
+ else:
+ return None
+
class SharedStructure(object):
def __init__(self, keys=None, length=0,
@@ -559,13 +705,13 @@
return self.structure.length
def iteritems(self):
- return self._as_rdict().iteritems()
+ return SharedItemIteratorImplementation(self.space, self)
def iterkeys(self):
- return self._as_rdict().iterkeys()
+ return SharedKeyIteratorImplementation(self.space, self)
def itervalues(self):
- return self._as_rdict().itervalues()
+ return SharedValueIteratorImplementation(self.space, self)
def keys(self):
space = self.space
@@ -593,6 +739,49 @@
return newimpl
+class SharedValueIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.values = dictimplementation.entries
+
+ def next(self):
+ if self.pos < self.len:
+ return self.values[self.pos]
+ else:
+ self.values = None
+ return None
+
+class SharedItemIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.structure.keys.iteritems()
+
+ def next_entry(self):
+ implementation = self.dictimplementation
+ assert isinstance(implementation, SharedDictImplementation)
+ for key, index in self.iterator:
+ if index >= 0:
+ w_value = implementation.entries[index]
+ return self.space.newtuple([self.space.wrap(key), w_value])
+ else:
+ return None
+
+class SharedKeyIteratorImplementation(IteratorImplementation):
+ def __init__(self, space, dictimplementation):
+ IteratorImplementation.__init__(self, space, dictimplementation)
+ self.iterator = dictimplementation.structure.keys.iteritems()
+
+ def next_entry(self):
+ implementation = self.dictimplementation
+ assert isinstance(implementation, SharedDictImplementation)
+ for key, index in self.iterator:
+ if index >= 0:
+ w_value = implementation.entries[index]
+ return self.space.wrap(key)
+ else:
+ return None
+
+
import time, py
class DictInfo(object):
@@ -710,15 +899,15 @@
def iteritems(self):
self.info.iteritems += 1
self.info.iterations += 1
- return self.content.iteritems()
+ return RDictItemIteratorImplementation(self.space, self)
def iterkeys(self):
self.info.iterkeys += 1
self.info.iterations += 1
- return self.content.iterkeys()
+ return RDictKeyIteratorImplementation(self.space, self)
def itervalues(self):
self.info.itervalues += 1
self.info.iterations += 1
- return self.content.itervalues()
+ return RDictValueIteratorImplementation(self.space, self)
def keys(self):
self.info.keys += 1
@@ -731,7 +920,9 @@
def items(self):
self.info.items += 1
self.info.listings += 1
- return self.content.items()
+ return [self.space.newtuple([w_key, w_val])
+ for w_key, w_val in self.content.iteritems()]
+
_example = DictInfo()
del DictInfo._dict_infos[-1]
@@ -783,9 +974,10 @@
def unwrap(w_dict, space):
result = {}
- for w_key, w_value in w_dict.implementation.iteritems():
- # generic mixed types unwrap
- result[space.unwrap(w_key)] = space.unwrap(w_value)
+ items = w_dict.implementation.items()
+ for w_pair in items:
+ key, val = space.unwrap(w_pair)
+ result[key] = val
return result
def len(w_self):
@@ -853,7 +1045,7 @@
dict_has_key__DictMulti_ANY = contains__DictMulti_ANY
def iter__DictMulti(space, w_dict):
- return W_DictMultiIter_Keys(space, w_dict.implementation)
+ return W_DictMultiIterObject(space, w_dict.implementation.iterkeys())
def eq__DictMulti_DictMulti(space, w_left, w_right):
if space.is_w(w_left, w_right):
@@ -861,7 +1053,13 @@
if w_left.implementation.length() != w_right.implementation.length():
return space.w_False
- for w_key, w_val in w_left.implementation.iteritems():
+ iteratorimplementation = w_left.implementation.iteritems()
+ while 1:
+ w_item = iteratorimplementation.next()
+ if w_item is None:
+ break
+ w_key = space.getitem(w_item, space.wrap(0))
+ w_val = space.getitem(w_item, space.wrap(1))
w_rightval = w_right.implementation.get(w_key)
if w_rightval is None:
return space.w_False
@@ -874,7 +1072,13 @@
returns the smallest key in acontent for which b's value is different or absent and this value """
w_smallest_diff_a_key = None
w_its_value = None
- for w_key, w_val in aimpl.iteritems():
+ iteratorimplementation = aimpl.iteritems()
+ while 1:
+ w_item = iteratorimplementation.next()
+ if w_item is None:
+ break
+ w_key = space.getitem(w_item, space.wrap(0))
+ w_val = space.getitem(w_item, space.wrap(1))
if w_smallest_diff_a_key is None or space.is_true(space.lt(w_key, w_smallest_diff_a_key)):
w_bvalue = bimpl.get(w_key)
if w_bvalue is None:
@@ -917,7 +1121,7 @@
return w_new
def dict_items__DictMulti(space, w_self):
- return space.newlist([space.newtuple([w_k, w_v]) for w_k, w_v in w_self.implementation.iteritems()])
+ return space.newlist(w_self.implementation.items())
def dict_keys__DictMulti(space, w_self):
return space.newlist(w_self.implementation.keys())
@@ -926,13 +1130,13 @@
return space.newlist(w_self.implementation.values())
def dict_iteritems__DictMulti(space, w_self):
- return W_DictMultiIter_Items(space, w_self.implementation)
+ return W_DictMultiIterObject(space, w_self.implementation.iteritems())
def dict_iterkeys__DictMulti(space, w_self):
- return W_DictMultiIter_Keys(space, w_self.implementation)
+ return W_DictMultiIterObject(space, w_self.implementation.iterkeys())
def dict_itervalues__DictMulti(space, w_self):
- return W_DictMultiIter_Values(space, w_self.implementation)
+ return W_DictMultiIterObject(space, w_self.implementation.itervalues())
def dict_clear__DictMulti(space, w_self):
w_self.implementation = space.emptydictimpl
@@ -977,74 +1181,29 @@
# ____________________________________________________________
# Iteration
+
class W_DictMultiIterObject(W_Object):
from pypy.objspace.std.dicttype import dictiter_typedef as typedef
- def __init__(w_self, space, implementation):
+ def __init__(w_self, space, iteratorimplementation):
w_self.space = space
- w_self.implementation = implementation
- w_self.len = implementation.length()
- w_self.pos = 0
- w_self.setup_iterator()
-
+ w_self.iteratorimplementation = iteratorimplementation
registerimplementation(W_DictMultiIterObject)
-class W_DictMultiIter_Keys(W_DictMultiIterObject):
- def setup_iterator(w_self):
- w_self.iterator = w_self.implementation.iterkeys()
- def next_entry(w_self):
- # note that this 'for' loop only runs once, at most
- for w_key in w_self.iterator:
- return w_key
- else:
- return None
-
-class W_DictMultiIter_Values(W_DictMultiIterObject):
- def setup_iterator(w_self):
- w_self.iterator = w_self.implementation.itervalues()
- def next_entry(w_self):
- # note that this 'for' loop only runs once, at most
- for w_value in w_self.iterator:
- return w_value
- else:
- return None
-
-class W_DictMultiIter_Items(W_DictMultiIterObject):
- def setup_iterator(w_self):
- w_self.iterator = w_self.implementation.iteritems()
- def next_entry(w_self):
- # note that this 'for' loop only runs once, at most
- for w_key, w_value in w_self.iterator:
- return w_self.space.newtuple([w_key, w_value])
- else:
- return None
-
-
def iter__DictMultiIterObject(space, w_dictiter):
return w_dictiter
def next__DictMultiIterObject(space, w_dictiter):
- implementation = w_dictiter.implementation
- if implementation is not None:
- if w_dictiter.len != implementation.length():
- w_dictiter.len = -1 # Make this error state sticky
- raise OperationError(space.w_RuntimeError,
- space.wrap("dictionary changed size during iteration"))
- # look for the next entry
- w_result = w_dictiter.next_entry()
- if w_result is not None:
- w_dictiter.pos += 1
- return w_result
- # no more entries
- w_dictiter.implementation = None
+ iteratorimplementation = w_dictiter.iteratorimplementation
+ w_result = iteratorimplementation.next()
+ if w_result is not None:
+ return w_result
raise OperationError(space.w_StopIteration, space.w_None)
def len__DictMultiIterObject(space, w_dictiter):
- implementation = w_dictiter.implementation
- if implementation is None or w_dictiter.len == -1:
- return space.wrap(0)
- return space.wrap(w_dictiter.len - w_dictiter.pos)
+ iteratorimplementation = w_dictiter.iteratorimplementation
+ return space.wrap(iteratorimplementation.length())
# ____________________________________________________________
Modified: pypy/dist/pypy/objspace/std/test/test_dictmultiobject.py
==============================================================================
--- pypy/dist/pypy/objspace/std/test/test_dictmultiobject.py (original)
+++ pypy/dist/pypy/objspace/std/test/test_dictmultiobject.py Thu Jan 4 00:47:53 2007
@@ -1,4 +1,5 @@
import autopath
+from pypy.interpreter.error import OperationError
from pypy.objspace.std.dictmultiobject import \
W_DictMultiObject, setitem__DictMulti_ANY_ANY, getitem__DictMulti_ANY, \
EmptyDictImplementation, RDictImplementation, StrDictImplementation, \
@@ -14,6 +15,20 @@
def setup_class(cls):
cls.space = gettestobjspace(**{"objspace.std.withmultidict": True})
+ def test_len_iter(self):
+ d = {1: 2, 3: 4}
+ i = iter(d)
+ assert len(i) == 2
+ x = i.next()
+ assert len(i) == 1
+ y = i.next()
+ assert len(i) == 0
+ l = [x, y]
+ l.sort()
+ assert l == [1, 3]
+ raises(StopIteration, i.next)
+ raises(StopIteration, i.next)
+
class TestW_DictSharing(test_dictobject.TestW_DictObject):
def setup_class(cls):
cls.space = gettestobjspace(**{"objspace.std.withsharingdict": True})
@@ -44,6 +59,12 @@
def isinstance(self, obj, klass):
return isinstance(obj, klass)
+ def newtuple(self, l):
+ return tuple(l)
+
+ w_StopIteration = StopIteration
+ w_None = None
+
class TestDictImplementation:
def setup_method(self,method):
self.space = FakeSpace()
@@ -124,21 +145,39 @@
def test_iterkeys(self):
self.impl.setitem(self.string, 1000)
self.impl.setitem(self.string2, 2000)
- keys = list(self.impl.iterkeys())
+ iteratorimplementation = self.impl.iterkeys()
+ keys = []
+ while 1:
+ key = iteratorimplementation.next()
+ if key is None:
+ break
+ keys.append(key)
keys.sort()
assert keys == [self.string, self.string2]
def test_itervalues(self):
self.impl.setitem(self.string, 1000)
self.impl.setitem(self.string2, 2000)
- values = list(self.impl.itervalues())
+ iteratorimplementation = self.impl.itervalues()
+ values = []
+ while 1:
+ value = iteratorimplementation.next()
+ if value is None:
+ break
+ values.append(value)
values.sort()
assert values == [1000, 2000]
def test_iteritems(self):
self.impl.setitem(self.string, 1000)
self.impl.setitem(self.string2, 2000)
- items = list(self.impl.iteritems())
+ iteratorimplementation = self.impl.iteritems()
+ items = []
+ while 1:
+ item = iteratorimplementation.next()
+ if item is None:
+ break
+ items.append(item)
items.sort()
assert items == zip([self.string, self.string2], [1000, 2000])
More information about the Pypy-commit
mailing list