[pypy-commit] pypy default: Test and fix for issue #2788. Also implements "del obj.getsetprop".
arigo
pypy.commits at gmail.com
Sun Apr 8 03:41:00 EDT 2018
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r94264:1843dd2013b4
Date: 2018-04-08 09:34 +0200
http://bitbucket.org/pypy/pypy/changeset/1843dd2013b4/
Log: Test and fix for issue #2788. Also implements "del obj.getsetprop".
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(TypeError, '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
@@ -309,12 +309,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_TypeError, "readonly attribute")
+ else:
+ raise oefmt(space.w_TypeError, "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_TypeError, "readonly attribute")
+ raise self.readonly_attribute(space)
try:
fset(self, space, w_obj, w_value)
except DescrMismatch:
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
@@ -1626,6 +1626,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/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -54,19 +54,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)
@@ -454,6 +461,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)
More information about the pypy-commit
mailing list