[pypy-commit] pypy py3.5: merge default into py3.5
mattip
pypy.commits at gmail.com
Mon Apr 9 17:31:22 EDT 2018
Author: Matti Picus <matti.picus at gmail.com>
Branch: py3.5
Changeset: r94285:e3384cb52177
Date: 2018-04-10 00:30 +0300
http://bitbucket.org/pypy/pypy/changeset/e3384cb52177/
Log: merge default into py3.5
diff --git a/pypy/doc/release-v6.0.0.rst b/pypy/doc/release-v6.0.0.rst
--- a/pypy/doc/release-v6.0.0.rst
+++ b/pypy/doc/release-v6.0.0.rst
@@ -8,26 +8,26 @@
the dual release.
This release is a feature release following our previous 5.10 incremental
-release in late December 2017, with many improvements in the C-API
-compatability layer and other improvements in speed and CPython compatibility.
-Since the changes affect the included python development header files, all
-c-extension modules must be recompiled for this version.
+release in late December 2017. Our C-API compatability layer ``cpyext`` is
+now much faster (see the `blog post`_) as well as more complete. We have made
+many other improvements in speed and CPython compatibility. Since the changes
+affect the included python development header files, all c-extension modules must
+be recompiled for this version.
-The Windows PyPy3.5 release is still considered beta-quality. There are issues
-with unicode handling especially around system calls and c-extensions.
+The Windows PyPy3.5 release is still considered beta-quality. There are open
+issues with unicode handling especially around system calls and c-extensions.
-The Matplotlib TkAgg backend now works with PyPy. PyGame and pygtk also now can
-work with PyPy.
+The Matplotlib TkAgg backend now works with PyPy, as do pygame and pygobject.
As always, this release is 100% compatible with the previous one and fixed
several issues and bugs raised by the growing community of PyPy users.
We strongly recommend updating.
+We updated the cffi module included in PyPy to version 1.11.5
+
The utf8 branch that changes internal representation of unicode to utf8 did not
-make it into the release. We also began working on a Python3.6 implementation,
-help is welcome.
-
-We updated the cffi module included in PyPy to version 1.11.5
+make it into the release, so there is still more goodness coming. We also
+began working on a Python3.6 implementation, help is welcome.
You can download the v6.0 releases here:
@@ -46,6 +46,7 @@
.. _`RPython`: https://rpython.readthedocs.org
.. _`modules`: project-ideas.html#make-more-python-modules-pypy-friendly
.. _`help`: project-ideas.html
+.. _`blog post`: https://morepypy.blogspot.it/2017/10/cape-of-good-hope-for-pypy-hello-from.html
What is PyPy?
=============
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -5,4 +5,8 @@
.. this is a revision shortly after release-pypy-6.0.0
.. startrev: 2e04adf1b89f
+.. branch: cpyext-subclass-setattr
+Fix for python-level classes that inherit from C-API types, previously the
+`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj`
+which led to cases where instance attributes were lost. Fixes issue #2793
diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py
--- a/pypy/interpreter/test/test_typedef.py
+++ b/pypy/interpreter/test/test_typedef.py
@@ -423,3 +423,10 @@
def test_get_with_none_arg(self):
raises(TypeError, type.__dict__['__mro__'].__get__, None)
raises(TypeError, type.__dict__['__mro__'].__get__, None, None)
+
+ def test_builtin_readonly_property(self):
+ import sys
+ x = lambda: 5
+ e = raises(AttributeError, 'x.func_globals = {}')
+ if '__pypy__' in sys.builtin_module_names:
+ assert str(e.value) == "readonly attribute 'func_globals'"
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -313,12 +313,18 @@
self.reqcls, Arguments(space, [w_obj,
space.newtext(self.name)]))
+ def readonly_attribute(self, space): # overwritten in cpyext
+ if self.name == '<generic property>':
+ raise oefmt(space.w_AttributeError, "readonly attribute")
+ else:
+ raise oefmt(space.w_AttributeError, "readonly attribute '%s'", self.name)
+
def descr_property_set(self, space, w_obj, w_value):
"""property.__set__(obj, value)
Change the value of the property of the given obj."""
fset = self.fset
if fset is None:
- raise oefmt(space.w_AttributeError, "readonly attribute")
+ raise self.readonly_attribute(space)
try:
fset(self, space, w_obj, w_value)
except DescrMismatch:
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -60,10 +60,10 @@
def _cpyext_attach_pyobj(self, space, py_obj):
self._cpy_ref = py_obj
- rawrefcount.create_link_pyobj(self, py_obj)
+ rawrefcount.create_link_pypy(self, py_obj)
cls._cpyext_attach_pyobj = _cpyext_attach_pyobj
-add_direct_pyobj_storage(W_BaseCPyObject)
+add_direct_pyobj_storage(W_BaseCPyObject)
add_direct_pyobj_storage(W_TypeObject)
add_direct_pyobj_storage(W_NoneObject)
add_direct_pyobj_storage(W_BoolObject)
@@ -415,3 +415,14 @@
@cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
def _Py_HashPointer(space, ptr):
return rffi.cast(lltype.Signed, ptr)
+
+ at cpython_api([PyObject], lltype.Void)
+def Py_IncRef(space, obj):
+ # used only ifdef PYPY_DEBUG_REFCOUNT
+ if obj:
+ incref(space, obj)
+
+ at cpython_api([PyObject], lltype.Void)
+def Py_DecRef(space, obj):
+ # used only ifdef PYPY_DEBUG_REFCOUNT
+ decref(space, obj)
diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c
--- a/pypy/module/cpyext/src/object.c
+++ b/pypy/module/cpyext/src/object.c
@@ -5,18 +5,6 @@
extern void _PyPy_Free(void *ptr);
extern void *_PyPy_Malloc(Py_ssize_t size);
-void
-Py_IncRef(PyObject *o)
-{
- Py_XINCREF(o);
-}
-
-void
-Py_DecRef(PyObject *o)
-{
- Py_XDECREF(o);
-}
-
/*
* The actual value of this variable will be the address of
* pyobject.w_marker_deallocating, and will be set by
diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c
--- a/pypy/module/cpyext/test/array.c
+++ b/pypy/module/cpyext/test/array.c
@@ -2936,6 +2936,87 @@
return PyLong_FromLong(obj1->ob_type->tp_dealloc == obj2->ob_type->tp_dealloc);
}
+static PyObject *
+subclass_with_attribute(PyObject *self, PyObject* args) {
+ /* what happens when we use tp_alloc to create the subclass, then
+ * assign to the w_obj via python, then get the GC to collect?
+ * The w_obj should not be collected!!
+ */
+ PyObject * obj, *sub, *attrib, *funcname, *attribname, *collect, *res, *tup;
+ PyTypeObject * subtype;
+ int i;
+ if (!PyArg_ParseTuple(args, "OOOO", &obj, &funcname, &attribname, &collect)) {
+ return NULL;
+ }
+ if (!PyType_Check(obj)) {
+ PyErr_SetString(PyExc_TypeError,
+ "expected type object");
+ return NULL;
+ }
+ subtype = (PyTypeObject*)obj;
+ sub = subtype->tp_alloc(subtype, 0);
+ if (!sub) {
+ return NULL;
+ }
+ attrib = PyObject_GetAttr(sub, funcname);
+ if (!attrib || (attrib == Py_None) ) {
+ PyErr_SetString(PyExc_ValueError,
+ "could not find function to call");
+ Py_XDECREF(attrib);
+ Py_DECREF(sub);
+ return NULL;
+ }
+ tup = PyTuple_New(0);
+ /*
+ #ifdef PYPY_VERSION
+ printf("calling addattrib pypylink %lu \n", sub->ob_pypy_link);
+ #endif
+ */
+ res = PyObject_Call(attrib, tup, NULL);
+ /*
+ #ifdef PYPY_VERSION
+ printf("after addattrib pypylink %lu \n", sub->ob_pypy_link);
+ #endif
+ */
+ Py_DECREF(attrib);
+ if (res == NULL) {
+ Py_DECREF(tup);
+ Py_DECREF(sub);
+ return NULL;
+ }
+ Py_DECREF(res);
+ for(i=0; i<10; i++) {
+ /*
+ #ifdef PYPY_VERSION
+ printf("starting loop iteration %d refcnt %lu pypylink %lu \n", i,
+ sub->ob_refcnt, sub->ob_pypy_link);
+ #else
+ printf("starting loop iteration %d refcnt %lu\n", i, sub->ob_refcnt);
+ #endif
+ */
+ attrib = PyObject_GetAttr(sub, attribname);
+ if (!attrib || (attrib == Py_None)) {
+ PyErr_SetString(PyExc_ValueError,
+ "could not find attrib on object");
+ Py_XDECREF(attrib);
+ Py_DECREF(tup);
+ Py_DECREF(sub);
+ return NULL;
+ }
+ Py_XDECREF(attrib);
+ res = PyObject_Call(collect, tup, NULL);
+ if (res == NULL) {
+ Py_DECREF(tup);
+ Py_DECREF(sub);
+ return NULL;
+ }
+ Py_DECREF(res);
+ }
+ Py_DECREF(tup);
+ Py_DECREF(sub);
+ Py_RETURN_NONE;
+}
+
/*********************** Install Module **************************/
static PyMethodDef a_methods[] = {
@@ -2948,6 +3029,7 @@
{"write_buffer_len", write_buffer_len, METH_O, NULL},
{"same_dealloc", (PyCFunction)same_dealloc, METH_VARARGS, NULL},
{"getitem", (PyCFunction)getitem, METH_VARARGS, NULL},
+ {"subclass_with_attribute", (PyCFunction)subclass_with_attribute, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} /* Sentinel */
};
diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py
--- a/pypy/module/cpyext/test/test_arraymodule.py
+++ b/pypy/module/cpyext/test/test_arraymodule.py
@@ -179,3 +179,15 @@
# array_subscr does)
raises(IndexError, module.getitem, a, -5)
+ def test_subclass_with_attribute(self):
+ module = self.import_module(name='array')
+ class Sub(module.array):
+ def addattrib(self):
+ print('called addattrib')
+ self.attrib = True
+ import gc
+ module.subclass_with_attribute(Sub, "addattrib", "attrib", gc.collect)
+ if self.runappdirect:
+ assert Sub.__module__ == 'pypy.module.cpyext.test.test_arraymodule'
+ assert str(Sub) == "<class 'pypy.module.cpyext.test.test_arraymodule.Sub'>"
+
diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py
--- a/pypy/module/cpyext/test/test_tupleobject.py
+++ b/pypy/module/cpyext/test/test_tupleobject.py
@@ -67,6 +67,18 @@
assert space.int_w(space.getitem(w_tuple, space.wrap(i))) == 42 + i
decref(space, ar[0])
+ py_tuple = state.ccall("PyTuple_New", 1)
+ ar[0] = py_tuple
+ api._PyTuple_Resize(ar, 1)
+ assert api.PyTuple_Size(ar[0]) == 1
+ decref(space, ar[0])
+
+ py_tuple = state.ccall("PyTuple_New", 1)
+ ar[0] = py_tuple
+ api._PyTuple_Resize(ar, 5)
+ assert api.PyTuple_Size(ar[0]) == 5
+ decref(space, ar[0])
+
lltype.free(ar, flavor='raw')
def test_setitem(self, space, api):
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -5,7 +5,7 @@
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.api import generic_cpy_call
from pypy.module.cpyext.pyobject import make_ref, from_ref, decref, as_pyobj
-from pypy.module.cpyext.typeobject import cts, PyTypeObjectPtr
+from pypy.module.cpyext.typeobject import cts, PyTypeObjectPtr, W_PyCTypeObject
@@ -410,33 +410,42 @@
def test_type_dict(self):
foo = self.import_module("foo")
module = self.import_extension('test', [
- ("hack_tp_dict", "METH_O",
+ ("hack_tp_dict", "METH_VARARGS",
'''
- PyTypeObject *type = args->ob_type;
+ PyTypeObject *type, *obj;
PyObject *a1 = PyLong_FromLong(1);
PyObject *a2 = PyLong_FromLong(2);
PyObject *value;
+ PyObject * key;
+ if (!PyArg_ParseTuple(args, "OO", &obj, &key))
+ return NULL;
+ type = obj->ob_type;
- if (PyDict_SetItemString(type->tp_dict, "a",
+ if (PyDict_SetItem(type->tp_dict, key,
a1) < 0)
return NULL;
Py_DECREF(a1);
PyType_Modified(type);
- value = PyObject_GetAttrString((PyObject *)type, "a");
+ value = PyObject_GetAttr((PyObject *)type, key);
Py_DECREF(value);
- if (PyDict_SetItemString(type->tp_dict, "a",
+ if (PyDict_SetItem(type->tp_dict, key,
a2) < 0)
return NULL;
Py_DECREF(a2);
PyType_Modified(type);
- value = PyObject_GetAttrString((PyObject *)type, "a");
+ value = PyObject_GetAttr((PyObject *)type, key);
return value;
'''
)
])
obj = foo.new()
- assert module.hack_tp_dict(obj) == 2
+ assert module.hack_tp_dict(obj, "a") == 2
+ class Sub(foo.fooType):
+ pass
+ obj = Sub()
+ assert module.hack_tp_dict(obj, "b") == 2
+
def test_tp_descr_get(self):
module = self.import_extension('foo', [
@@ -572,6 +581,23 @@
def test_typeslots(self, space):
assert cts.macros['Py_tp_doc'] == 56
+ def test_subclass_not_PyCTypeObject(self, space, api):
+ pyobj = make_ref(space, api.PyLong_Type)
+ py_type = rffi.cast(PyTypeObjectPtr, pyobj)
+ w_pyclass = W_PyCTypeObject(space, py_type)
+ w_class = space.appexec([w_pyclass], """(base):
+ class Sub(base):
+ def addattrib(self, value):
+ self.attrib = value
+ return Sub
+ """)
+ assert w_pyclass in w_class.mro_w
+ assert isinstance(w_pyclass, W_PyCTypeObject)
+ assert not isinstance(w_class, W_PyCTypeObject)
+ assert w_pyclass.is_cpytype()
+ # XXX document the current status, not clear if this is desirable
+ assert w_class.is_cpytype()
+
class AppTestSlots(AppTestCpythonExtensionBase):
def setup_class(cls):
@@ -1558,6 +1584,64 @@
pass
C(42) # assert is not aborting
+ def test_getset(self):
+ module = self.import_extension('foo', [
+ ("get_instance", "METH_NOARGS",
+ '''
+ return PyObject_New(PyObject, &Foo_Type);
+ '''
+ ), ("get_number", "METH_NOARGS",
+ '''
+ return PyInt_FromLong(my_global_number);
+ '''
+ )], prologue='''
+ #if PY_MAJOR_VERSION > 2
+ #define PyInt_FromLong PyLong_FromLong
+ #endif
+ static long my_global_number;
+ static PyTypeObject Foo_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "foo.foo",
+ };
+ static PyObject *bar_get(PyObject *foo, void *closure)
+ {
+ return PyInt_FromLong(1000 + (long)closure);
+ }
+ static PyObject *baz_get(PyObject *foo, void *closure)
+ {
+ return PyInt_FromLong(2000 + (long)closure);
+ }
+ static int baz_set(PyObject *foo, PyObject *x, void *closure)
+ {
+ if (x != NULL)
+ my_global_number = 3000 + (long)closure + PyInt_AsLong(x);
+ else
+ my_global_number = 4000 + (long)closure;
+ return 0;
+ }
+ static PyGetSetDef foo_getset[] = {
+ { "bar", bar_get, NULL, "mybardoc", (void *)42 },
+ { "baz", baz_get, baz_set, "mybazdoc", (void *)43 },
+ { NULL }
+ };
+ ''', more_init = '''
+ Foo_Type.tp_getset = foo_getset;
+ Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT;
+ if (PyType_Ready(&Foo_Type) < 0) INITERROR;
+ ''')
+ foo = module.get_instance()
+ assert foo.bar == 1042
+ assert foo.bar == 1042
+ assert foo.baz == 2043
+ foo.baz = 50000
+ assert module.get_number() == 53043
+ e = raises(AttributeError, "foo.bar = 0")
+ assert str(e.value).startswith("attribute 'bar' of '")
+ assert str(e.value).endswith("foo' objects is not writable")
+ del foo.baz
+ assert module.get_number() == 4043
+ raises(AttributeError, "del foo.bar")
+
class AppTestHashable(AppTestCpythonExtensionBase):
def test_unhashable(self):
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -187,6 +187,8 @@
PyErr_BadInternalCall(space)
oldref = rffi.cast(PyTupleObject, ref)
oldsize = oldref.c_ob_size
+ if oldsize == newsize:
+ return 0
ptup = state.ccall("PyTuple_New", newsize)
if not ptup:
state.check_and_raise_exception(always=True)
@@ -199,8 +201,9 @@
to_cp = newsize
for i in range(to_cp):
ob = oldref.c_ob_item[i]
- incref(space, ob)
- newref.c_ob_item[i] = ob
+ if ob:
+ incref(space, ob)
+ newref.c_ob_item[i] = ob
except:
decref(space, p_ref[0])
p_ref[0] = lltype.nullptr(PyObject.TO)
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -1,5 +1,5 @@
from rpython.rlib.unroll import unrolling_iterable
-from rpython.rlib import jit
+from rpython.rlib import jit, rawrefcount
from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rtyper.lltypesystem import rffi, lltype
@@ -57,19 +57,26 @@
class W_GetSetPropertyEx(GetSetProperty):
def __init__(self, getset, w_type):
self.getset = getset
- self.name = rffi.charp2str(getset.c_name)
self.w_type = w_type
- doc = set = get = None
+ doc = fset = fget = fdel = None
if doc:
# XXX dead code?
doc = rffi.charp2str(getset.c_doc)
if getset.c_get:
- get = GettersAndSetters.getter.im_func
+ fget = GettersAndSetters.getter.im_func
if getset.c_set:
- set = GettersAndSetters.setter.im_func
- GetSetProperty.__init__(self, get, set, None, doc,
+ fset = GettersAndSetters.setter.im_func
+ fdel = GettersAndSetters.deleter.im_func
+ GetSetProperty.__init__(self, fget, fset, fdel, doc,
cls=None, use_closure=True,
tag="cpyext_1")
+ self.name = rffi.charp2str(getset.c_name)
+
+ def readonly_attribute(self, space): # overwritten
+ raise oefmt(space.w_AttributeError,
+ "attribute '%s' of '%N' objects is not writable",
+ self.name, self.w_type)
+
def PyDescr_NewGetSet(space, getset, w_type):
return W_GetSetPropertyEx(getset, w_type)
@@ -455,6 +462,16 @@
state = space.fromcache(State)
state.check_and_raise_exception()
+ def deleter(self, space, w_self):
+ assert isinstance(self, W_GetSetPropertyEx)
+ check_descr(space, w_self, self.w_type)
+ res = generic_cpy_call(
+ space, self.getset.c_set, w_self, None,
+ self.getset.c_closure)
+ if rffi.cast(lltype.Signed, res) < 0:
+ state = space.fromcache(State)
+ state.check_and_raise_exception()
+
def member_getter(self, space, w_self):
assert isinstance(self, W_MemberDescr)
check_descr(space, w_self, self.w_type)
@@ -518,6 +535,10 @@
self.w_doc = space.newtext_or_none(extract_doc(rawdoc, name))
self.text_signature = extract_txtsig(rawdoc, name)
+ def _cpyext_attach_pyobj(self, space, py_obj):
+ self._cpy_ref = py_obj
+ rawrefcount.create_link_pyobj(self, py_obj)
+
@bootstrap_function
def init_typeobject(space):
make_typedescr(space.w_type.layout.typedef,
@@ -667,7 +688,6 @@
try:
w_obj = _type_realize(space, py_obj)
finally:
- name = rffi.charp2str(cts.cast('char*', pto.c_tp_name))
pto.c_tp_flags &= ~Py_TPFLAGS_READYING
pto.c_tp_flags |= Py_TPFLAGS_READY
return w_obj
@@ -762,7 +782,6 @@
base = pto.c_tp_base
base_pyo = rffi.cast(PyObject, pto.c_tp_base)
if base and not base.c_tp_flags & Py_TPFLAGS_READY:
- name = rffi.charp2str(cts.cast('char*', base.c_tp_name))
type_realize(space, base_pyo)
if base and not pto.c_ob_type: # will be filled later
pto.c_ob_type = base.c_ob_type
More information about the pypy-commit
mailing list