Author: Ronan Lamy <[email protected]>
Branch:
Changeset: r96095:5062f3687585
Date: 2019-02-19 16:04 +0000
http://bitbucket.org/pypy/pypy/changeset/5062f3687585/
Log: Call internal methods from PyDict_XXXItem() instead of going through
dunder methods (CPython compatibility)
Fixes issue #2954
diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py
--- a/pypy/module/cpyext/dictobject.py
+++ b/pypy/module/cpyext/dictobject.py
@@ -2,6 +2,7 @@
from rpython.rlib.objectmodel import specialize
from pypy.interpreter.error import OperationError
from pypy.objspace.std.classdict import ClassDictStrategy
+from pypy.objspace.std.dictmultiobject import W_DictMultiObject
from pypy.interpreter.typedef import GetSetProperty
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, build_type_checkers_flags, Py_ssize_t,
@@ -71,68 +72,59 @@
@cpython_api([PyObject, PyObject], PyObject, error=CANNOT_FAIL,
result_borrowed=True)
def PyDict_GetItem(space, w_dict, w_key):
- try:
- w_res = space.getitem(w_dict, w_key)
- except:
- return None
+ if not isinstance(w_dict, W_DictMultiObject):
+ raise PyErr_BadInternalCall(space)
# NOTE: this works so far because all our dict strategies store
# *values* as full objects, which stay alive as long as the dict is
# alive and not modified. So we can return a borrowed ref.
# XXX this is wrong with IntMutableCell. Hope it works...
- return w_res
+ return w_dict.getitem(w_key)
@cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1)
def PyDict_SetItem(space, w_dict, w_key, w_obj):
- if PyDict_Check(space, w_dict):
- space.setitem(w_dict, w_key, w_obj)
- return 0
- else:
- PyErr_BadInternalCall(space)
+ if not isinstance(w_dict, W_DictMultiObject):
+ raise PyErr_BadInternalCall(space)
+ w_dict.setitem(w_key, w_obj)
+ return 0
@cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
def PyDict_DelItem(space, w_dict, w_key):
- if PyDict_Check(space, w_dict):
- space.delitem(w_dict, w_key)
- return 0
- else:
- PyErr_BadInternalCall(space)
+ if not isinstance(w_dict, W_DictMultiObject):
+ raise PyErr_BadInternalCall(space)
+ w_dict.descr_delitem(space, w_key)
+ return 0
@cpython_api([PyObject, CONST_STRING, PyObject], rffi.INT_real, error=-1)
def PyDict_SetItemString(space, w_dict, key_ptr, w_obj):
- if PyDict_Check(space, w_dict):
- key = rffi.charp2str(key_ptr)
- space.setitem_str(w_dict, key, w_obj)
- return 0
- else:
- PyErr_BadInternalCall(space)
+ w_key = space.newtext(rffi.charp2str(key_ptr))
+ if not isinstance(w_dict, W_DictMultiObject):
+ raise PyErr_BadInternalCall(space)
+ w_dict.setitem(w_key, w_obj)
+ return 0
@cpython_api([PyObject, CONST_STRING], PyObject, error=CANNOT_FAIL,
result_borrowed=True)
def PyDict_GetItemString(space, w_dict, key):
"""This is the same as PyDict_GetItem(), but key is specified as a
char*, rather than a PyObject*."""
- try:
- w_res = space.finditem_str(w_dict, rffi.charp2str(key))
- except:
- w_res = None
+ w_key = space.newtext(rffi.charp2str(key))
+ if not isinstance(w_dict, W_DictMultiObject):
+ raise PyErr_BadInternalCall(space)
# NOTE: this works so far because all our dict strategies store
# *values* as full objects, which stay alive as long as the dict is
# alive and not modified. So we can return a borrowed ref.
# XXX this is wrong with IntMutableCell. Hope it works...
- return w_res
+ return w_dict.getitem(w_key)
@cpython_api([PyObject, CONST_STRING], rffi.INT_real, error=-1)
def PyDict_DelItemString(space, w_dict, key_ptr):
"""Remove the entry in dictionary p which has a key specified by the string
key. Return 0 on success or -1 on failure."""
- if PyDict_Check(space, w_dict):
- key = rffi.charp2str(key_ptr)
- # our dicts dont have a standardized interface, so we need
- # to go through the space
- space.delitem(w_dict, space.newtext(key))
- return 0
- else:
- PyErr_BadInternalCall(space)
+ w_key = space.newtext(rffi.charp2str(key_ptr))
+ if not isinstance(w_dict, W_DictMultiObject):
+ raise PyErr_BadInternalCall(space)
+ w_dict.descr_delitem(space, w_key)
+ return 0
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PyDict_Size(space, w_obj):
@@ -182,7 +174,7 @@
"""
override = rffi.cast(lltype.Signed, override)
w_keys = space.call_method(w_b, "keys")
- for w_key in space.iteriterable(w_keys):
+ for w_key in space.iteriterable(w_keys):
if not _has_val(space, w_a, w_key) or override != 0:
space.setitem(w_a, w_key, space.getitem(w_b, w_key))
return 0
diff --git a/pypy/module/cpyext/test/test_dictobject.py
b/pypy/module/cpyext/test/test_dictobject.py
--- a/pypy/module/cpyext/test/test_dictobject.py
+++ b/pypy/module/cpyext/test/test_dictobject.py
@@ -310,3 +310,67 @@
assert module.dict_delitem(d, 'a') == 0
r = module.dict_next({'a': 1, 'b': 2})
assert r == 2
+
+ def test_subclassing(self):
+ module = self.import_extension('foo', [
+ ("dict_setitem", "METH_VARARGS",
+ """
+ PyObject *d, *key, *value;
+ if (!PyArg_ParseTuple(args, "OOO", &d, &key, &value)) {
+ return NULL;
+ }
+ if (PyDict_SetItem(d, key, value) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ """),
+ ("dict_delitem", "METH_VARARGS",
+ """
+ PyObject *d, *key;
+ if (!PyArg_ParseTuple(args, "OO", &d, &key)) {
+ return NULL;
+ }
+ if (PyDict_DelItem(d, key) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ """),
+ ("dict_getitem", "METH_VARARGS",
+ """
+ PyObject *d, *key, *result;
+ if (!PyArg_ParseTuple(args, "OO", &d, &key)) {
+ return NULL;
+ }
+ result = PyDict_GetItem(d, key);
+ Py_XINCREF(result);
+ return result;
+ """),
+ ])
+
+ class mydict(dict):
+ def __setitem__(self, key, value):
+ dict.__setitem__(self, key, 42)
+
+ def __delitem__(self, key):
+ dict.__setitem__(self, key, None)
+ d = {}
+ module.dict_setitem(d, 1, 2)
+ assert d[1] == 2
+ d = mydict()
+ d[1] = 2
+ assert d[1] == 42
+ module.dict_setitem(d, 2, 3)
+ assert d[2] == 3
+ del d[2]
+ assert d[2] is None
+ module.dict_delitem(d, 2)
+ assert 2 not in d
+
+ class mydict2(dict):
+ def __getitem__(self, key):
+ return 42
+
+ d = mydict2()
+ d[1] = 2
+ assert d[1] == 42
+ assert module.dict_getitem(d, 1) == 2
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit