Author: Armin Rigo <[email protected]>
Branch: py3.5
Changeset: r90080:82e97c392624
Date: 2017-02-13 14:33 +0100
http://bitbucket.org/pypy/pypy/changeset/82e97c392624/
Log: hg merge default
diff --git a/lib-python/2.7/collections.py b/lib-python/2.7/collections.py
--- a/lib-python/2.7/collections.py
+++ b/lib-python/2.7/collections.py
@@ -33,6 +33,10 @@
from __pypy__ import reversed_dict as _reversed_dict
except ImportError:
_reversed_dict = None # don't have ordered dicts
+try:
+ from __pypy__ import dict_popitem_first as _dict_popitem_first
+except ImportError:
+ _dict_popitem_first = None
try:
from thread import get_ident as _get_ident
@@ -44,6 +48,17 @@
### OrderedDict
################################################################################
+if _dict_popitem_first is None:
+ def _dict_popitem_first(self):
+ it = dict.iteritems(self)
+ try:
+ k, v = it.next()
+ except StopIteration:
+ raise KeyError('dictionary is empty')
+ dict.__delitem__(self, k)
+ return (k, v)
+
+
class OrderedDict(dict):
'''Dictionary that remembers insertion order.
@@ -68,12 +83,7 @@
if last:
return dict.popitem(self)
else:
- it = dict.__iter__(self)
- try:
- k = it.next()
- except StopIteration:
- raise KeyError('dictionary is empty')
- return (k, self.pop(k))
+ return _dict_popitem_first(self)
def __repr__(self, _repr_running={}):
'od.__repr__() <==> repr(od)'
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -78,6 +78,7 @@
'add_memory_pressure' : 'interp_magic.add_memory_pressure',
'newdict' : 'interp_dict.newdict',
'reversed_dict' : 'interp_dict.reversed_dict',
+ 'dict_popitem_first' : 'interp_dict.dict_popitem_first',
'delitem_if_value_is' : 'interp_dict.delitem_if_value_is',
'move_to_end' : 'interp_dict.move_to_end',
'strategy' : 'interp_magic.strategy', # dict,set,list
diff --git a/pypy/module/__pypy__/interp_dict.py
b/pypy/module/__pypy__/interp_dict.py
--- a/pypy/module/__pypy__/interp_dict.py
+++ b/pypy/module/__pypy__/interp_dict.py
@@ -45,6 +45,27 @@
raise OperationError(space.w_TypeError, space.w_None)
return w_obj.nondescr_reversed_dict(space)
+def dict_popitem_first(space, w_obj):
+ """Interp-level implementation of OrderedDict.popitem(last=False).
+ """
+ from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+ if not isinstance(w_obj, W_DictMultiObject):
+ raise OperationError(space.w_TypeError, space.w_None)
+ return w_obj.nondescr_popitem_first(space)
+
+def delitem_if_value_is(space, w_obj, w_key, w_value):
+ """Atomic equivalent to: 'if dict.get(key) is value: del dict[key]'.
+
+ SPECIAL USE CASES ONLY! Avoid using on dicts which are specialized,
+ e.g. to int or str keys, because it switches to the object strategy.
+ Also, the 'is' operation is really pointer equality, so avoid using
+ it if 'value' is an immutable object like int or str.
+ """
+ from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+ if not isinstance(w_obj, W_DictMultiObject):
+ raise OperationError(space.w_TypeError, space.w_None)
+ return w_obj.nondescr_delitem_if_value_is(space, w_key, w_value)
+
@unwrap_spec(last=bool)
def move_to_end(space, w_obj, w_key, last=True):
"""Move the key in a dictionary object into the first or last position.
@@ -59,16 +80,3 @@
if not isinstance(w_obj, W_DictMultiObject):
raise OperationError(space.w_TypeError, space.w_None)
return w_obj.nondescr_move_to_end(space, w_key, last)
-
-def delitem_if_value_is(space, w_obj, w_key, w_value):
- """Atomic equivalent to: 'if dict.get(key) is value: del dict[key]'.
-
- SPECIAL USE CASES ONLY! Avoid using on dicts which are specialized,
- e.g. to int or str keys, because it switches to the object strategy.
- Also, the 'is' operation is really pointer equality, so avoid using
- it if 'value' is an immutable object like int or str.
- """
- from pypy.objspace.std.dictmultiobject import W_DictMultiObject
- if not isinstance(w_obj, W_DictMultiObject):
- raise OperationError(space.w_TypeError, space.w_None)
- return w_obj.nondescr_delitem_if_value_is(space, w_key, w_value)
diff --git a/pypy/objspace/std/dictmultiobject.py
b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -102,13 +102,6 @@
result[key] = val
return result
- def missing_method(w_dict, space, w_key):
- if not space.is_w(space.type(w_dict), space.w_dict):
- w_missing = space.lookup(w_dict, '__missing__')
- if w_missing is not None:
- return space.get_and_call_function(w_missing, w_dict, w_key)
- return None
-
def initialize_content(self, list_pairs_w):
for w_k, w_v in list_pairs_w:
self.setitem(w_k, w_v)
@@ -191,9 +184,11 @@
if w_value is not None:
return w_value
- w_missing_item = self.missing_method(space, w_key)
- if w_missing_item is not None:
- return w_missing_item
+ # if there is a __missing__ method, call it
+ if not space.is_w(space.type(self), space.w_dict):
+ w_missing = space.lookup(self, '__missing__')
+ if w_missing is not None:
+ return space.get_and_call_function(w_missing, self, w_key)
space.raise_key_error(w_key)
@@ -206,6 +201,14 @@
except KeyError:
space.raise_key_error(w_key)
+ def internal_delitem(self, w_key):
+ try:
+ self.delitem(w_key)
+ except KeyError:
+ raise oefmt(self.space.w_RuntimeError,
+ "an internal 'del' on the dictionary failed to find "
+ "the key")
+
def descr_copy(self, space):
"""D.copy() -> a shallow copy of D"""
return self.copy()
@@ -234,6 +237,14 @@
w_keys = self.w_keys()
return space.call_method(w_keys, '__reversed__')
+ def nondescr_delitem_if_value_is(self, space, w_key, w_value):
+ """Not exposed directly to app-level, but used by
+ _weakref._remove_dead_weakref and via __pypy__.delitem_if_value_is().
+ """
+ strategy = self.ensure_object_strategy()
+ d = strategy.unerase(self.dstorage)
+ objectmodel.delitem_if_value_is(d, w_key, w_value)
+
def nondescr_move_to_end(self, space, w_key, last_flag):
"""Not exposed directly to app-level, but via __pypy__.move_to_end().
"""
@@ -246,7 +257,7 @@
if w_value is None:
space.raise_key_error(w_key)
else:
- self.delitem(w_key)
+ self.internal_delitem(w_key)
if last_flag:
self.setitem(w_key, w_value)
else:
@@ -265,13 +276,14 @@
for i in range(len(keys_w)):
self.setitem(keys_w[i], values_w[i])
- def nondescr_delitem_if_value_is(self, space, w_key, w_value):
- """Not exposed directly to app-level, but used by
- _weakref._remove_dead_weakref and via __pypy__.delitem_if_value_is().
+ def nondescr_popitem_first(self, space):
+ """Not exposed directly to app-level, but via __pypy__.popitem_first().
"""
- strategy = self.ensure_object_strategy()
- d = strategy.unerase(self.dstorage)
- objectmodel.delitem_if_value_is(d, w_key, w_value)
+ w_key, w_value = self.iteritems().next_item()
+ if w_key is None:
+ raise oefmt(space.w_KeyError, "popitem(): dictionary is empty")
+ self.internal_delitem(w_key)
+ return space.newtuple([w_key, w_value])
def descr_clear(self, space):
"""D.clear() -> None. Remove all items from D."""
@@ -288,6 +300,13 @@
corresponding value\nIf key is not found, d is returned if given,
otherwise KeyError is raised
"""
+ strategy = self.get_strategy()
+ if strategy.has_pop:
+ try:
+ return strategy.pop(self, w_key, w_default)
+ except KeyError:
+ raise space.raise_key_error(w_key)
+ # fall-back
w_item = self.getitem(w_key)
if w_item is None:
if w_default is not None:
@@ -295,7 +314,7 @@
else:
space.raise_key_error(w_key)
else:
- self.delitem(w_key)
+ self.internal_delitem(w_key)
return w_item
def descr_popitem(self, space):
@@ -536,8 +555,8 @@
has_iterreversed = False
has_move_to_end = False
- # no 'getiterreversed' and no 'move_to_end': no default
- # implementation available
+ has_pop = False
+ # ^^^ no default implementation available for these methods
def rev_update1_dict_dict(self, w_dict, w_updatedict):
iteritems = self.iteritems(w_dict)
@@ -824,6 +843,9 @@
if hasattr(dictimpl, 'move_to_end'):
dictimpl.has_move_to_end = True
+ if hasattr(dictimpl, 'pop'):
+ dictimpl.has_pop = True
+
@jit.look_inside_iff(lambda self, w_dict, w_updatedict:
w_dict_unrolling_heuristic(w_dict))
def rev_update1_dict_dict(self, w_dict, w_updatedict):
@@ -977,6 +999,21 @@
key, value = self.unerase(w_dict.dstorage).popitem()
return (self.wrap(key), value)
+ def pop(self, w_dict, w_key, w_default):
+ space = self.space
+ if self.is_correct_type(w_key):
+ key = self.unwrap(w_key)
+ d = self.unerase(w_dict.dstorage)
+ if w_default is None:
+ return d.pop(key)
+ else:
+ return d.pop(key, w_default)
+ elif self._never_equal_to(space.type(w_key)):
+ raise KeyError
+ else:
+ self.switch_to_object_strategy(w_dict)
+ return w_dict.get_strategy().pop(w_dict, w_key, w_default)
+
def clear(self, w_dict):
self.unerase(w_dict.dstorage).clear()
diff --git a/pypy/objspace/std/test/test_dictmultiobject.py
b/pypy/objspace/std/test/test_dictmultiobject.py
--- a/pypy/objspace/std/test/test_dictmultiobject.py
+++ b/pypy/objspace/std/test/test_dictmultiobject.py
@@ -266,10 +266,36 @@
d = {1: 2, 3: 4, 5: 6}
it = __pypy__.reversed_dict(d)
key = next(it)
- assert key in [1, 3, 5]
+ assert key in [1, 3, 5] # on CPython, dicts are not ordered
del d[key]
raises(RuntimeError, next, it)
+ def test_dict_popitem_first(self):
+ import __pypy__
+ d = {"a": 5}
+ assert __pypy__.dict_popitem_first(d) == ("a", 5)
+ raises(KeyError, __pypy__.dict_popitem_first, d)
+
+ def kwdict(**k):
+ return k
+ d = kwdict(a=55)
+ assert __pypy__.dict_popitem_first(d) == ("a", 55)
+ raises(KeyError, __pypy__.dict_popitem_first, d)
+
+ def test_delitem_if_value_is(self):
+ import __pypy__
+ class X:
+ pass
+ x2 = X()
+ x3 = X()
+ d = {2: x2, 3: x3}
+ __pypy__.delitem_if_value_is(d, 2, x3)
+ assert d == {2: x2, 3: x3}
+ __pypy__.delitem_if_value_is(d, 2, x2)
+ assert d == {3: x3}
+ __pypy__.delitem_if_value_is(d, 2, x3)
+ assert d == {3: x3}
+
def test_move_to_end(self):
import __pypy__
raises(KeyError, __pypy__.move_to_end, {}, 'foo')
@@ -294,20 +320,6 @@
assert list(d) == [key] + other_keys
raises(KeyError, __pypy__.move_to_end, d, key * 3, last=last)
- def test_delitem_if_value_is(self):
- import __pypy__
- class X:
- pass
- x2 = X()
- x3 = X()
- d = {2: x2, 3: x3}
- __pypy__.delitem_if_value_is(d, 2, x3)
- assert d == {2: x2, 3: x3}
- __pypy__.delitem_if_value_is(d, 2, x2)
- assert d == {3: x3}
- __pypy__.delitem_if_value_is(d, 2, x3)
- assert d == {3: x3}
-
def test_keys(self):
d = {1: 2, 3: 4}
kys = list(d.keys())
@@ -618,6 +630,18 @@
else:
assert False, 'Expected KeyError'
+ def test_pop_switching_strategy(self):
+ class Foo:
+ def __hash__(self):
+ return hash("a")
+ def __eq__(self, other):
+ return other == "a"
+ d = {"a": 42}
+ x = d.pop(Foo())
+ assert x == 42 and len(d) == 0
+ d = {"b": 43}
+ raises(KeyError, d.pop, Foo())
+
def test_no_len_on_dict_iter(self):
iterable = {1: 2, 3: 4}
raises(TypeError, len, iter(iterable))
@@ -694,6 +718,31 @@
setattr(a, s, 123)
assert holder.seen is s
+ def test_internal_delitem(self):
+ class K:
+ def __hash__(self):
+ return 42
+ def __eq__(self, other):
+ if is_equal[0]:
+ is_equal[0] -= 1
+ return True
+ return False
+ is_equal = [0]
+ k1 = K()
+ k2 = K()
+ d = {k1: 1, k2: 2}
+ k3 = K()
+ is_equal = [1]
+ try:
+ x = d.pop(k3)
+ except RuntimeError:
+ # This used to give a Fatal RPython error: KeyError.
+ # Now at least it should raise an app-level RuntimeError,
+ # or just work.
+ assert len(d) == 2
+ else:
+ assert (x == 1 or x == 2) and len(d) == 1
+
class AppTestDictViews:
def test_dictview(self):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit