[pypy-commit] pypy cpyext-ext: copy add_docstring from numpy, add test that passes with -A

mattip pypy.commits at gmail.com
Sat Jan 9 18:13:03 EST 2016


Author: mattip <matti.picus at gmail.com>
Branch: cpyext-ext
Changeset: r81649:94e1a7e213d9
Date: 2016-01-10 01:10 +0200
http://bitbucket.org/pypy/pypy/changeset/94e1a7e213d9/

Log:	copy add_docstring from numpy, add test that passes with -A

diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c
--- a/pypy/module/cpyext/test/foo.c
+++ b/pypy/module/cpyext/test/foo.c
@@ -20,6 +20,7 @@
     long long foo_longlong;
     unsigned long long foo_ulonglong;
     Py_ssize_t foo_ssizet;
+    PyObject * foo_docless;
 } fooobject;
 
 static PyTypeObject footype;
@@ -184,6 +185,7 @@
     {"longlong_member", T_LONGLONG, offsetof(fooobject, foo_longlong), 0, NULL},
     {"ulonglong_member", T_ULONGLONG, offsetof(fooobject, foo_ulonglong), 0, NULL},  
     {"ssizet_member", T_PYSSIZET, offsetof(fooobject, foo_ssizet), 0, NULL},
+    {"docless_member", T_OBJECT, offsetof(fooobject, foo_docless), READONLY, NULL},
     {NULL}  /* Sentinel */
 };
 
@@ -623,12 +625,107 @@
     (destructor)custom_dealloc, /*tp_dealloc*/
 };
 
+static PyObject * add_docstring(PyObject * self, PyObject * args)
+{
+    PyObject *obj;
+    PyObject *str;
+    char *docstr;
+    static char *msg = "already has a docstring";
+    PyObject *tp_dict = footype.tp_dict;
+    PyObject *myobj;
+    static PyTypeObject *PyMemberDescr_TypePtr = NULL;
+    static PyTypeObject *PyGetSetDescr_TypePtr = NULL;
+    static PyTypeObject *PyMethodDescr_TypePtr = NULL;
+
+    /* Don't add docstrings */
+    if (Py_OptimizeFlag > 1) {
+        Py_RETURN_NONE;
+    }
+
+    if (PyGetSetDescr_TypePtr == NULL) {
+        /* Get "subdescr" */
+        myobj = PyDict_GetItemString(tp_dict, "name");
+        if (myobj != NULL) {
+            PyGetSetDescr_TypePtr = Py_TYPE(myobj);
+        }
+    }
+    if (PyMemberDescr_TypePtr == NULL) {
+        myobj = PyDict_GetItemString(tp_dict, "int_member");
+        if (myobj != NULL) {
+            PyMemberDescr_TypePtr = Py_TYPE(myobj);
+        }
+    }
+    if (PyMethodDescr_TypePtr == NULL) {
+        myobj = PyDict_GetItemString(tp_dict, "classmeth");
+        if (myobj != NULL) {
+            PyMethodDescr_TypePtr = Py_TYPE(myobj);
+        }
+    }
+
+    if (!PyArg_ParseTuple(args, "OO!", &obj, &PyString_Type, &str)) {
+        return NULL;
+    }
+    docstr = PyString_AS_STRING(str);
+#define _TESTDOC1(typebase) (Py_TYPE(obj) == &Py##typebase##_Type)
+#define _TESTDOC2(typebase) (Py_TYPE(obj) == Py##typebase##_TypePtr)
+#define _ADDDOC(typebase, doc, name) do {                               \
+        Py##typebase##Object *new = (Py##typebase##Object *)obj;        \
+        if (!(doc)) {                                                   \
+            doc = docstr;                                               \
+        }                                                               \
+        else {                                                          \
+            PyErr_Format(PyExc_RuntimeError, "%s method %s", name, msg); \
+            return NULL;                                                \
+        }                                                               \
+    } while (0)
+
+    if (_TESTDOC1(CFunction)) {
+        _ADDDOC(CFunction, new->m_ml->ml_doc, new->m_ml->ml_name);
+    }
+    else if (_TESTDOC1(Type)) {
+        _ADDDOC(Type, new->tp_doc, new->tp_name);
+    }
+    else if (_TESTDOC2(MemberDescr)) {
+        _ADDDOC(MemberDescr, new->d_member->doc, new->d_member->name);
+    }
+    else if (_TESTDOC2(GetSetDescr)) {
+        _ADDDOC(GetSetDescr, new->d_getset->doc, new->d_getset->name);
+    }
+    else if (_TESTDOC2(MethodDescr)) {
+        _ADDDOC(MethodDescr, new->d_method->ml_doc, new->d_method->ml_name);
+    }
+    else {
+        PyObject *doc_attr;
+
+        doc_attr = PyObject_GetAttrString(obj, "__doc__");
+        if (doc_attr != NULL && doc_attr != Py_None) {
+            PyErr_Format(PyExc_RuntimeError, "object %s", msg);
+            return NULL;
+        }
+        Py_XDECREF(doc_attr);
+
+        if (PyObject_SetAttrString(obj, "__doc__", str) < 0) {
+            PyErr_SetString(PyExc_TypeError,
+                            "Cannot set a docstring for that object");
+            return NULL;
+        }
+        Py_RETURN_NONE;
+    }
+
+#undef _TESTDOC1
+#undef _TESTDOC2
+#undef _ADDDOC
+
+    Py_INCREF(str);
+    Py_RETURN_NONE;
+}
 
 /* List of functions exported by this module */
 
 static PyMethodDef foo_functions[] = {
     {"new",        (PyCFunction)foo_new, METH_NOARGS, NULL},
     {"newCustom",  (PyCFunction)newCustom, METH_NOARGS, NULL},
+    {"add_docstring",  (PyCFunction)add_docstring, METH_VARARGS, NULL},
     {NULL,        NULL}    /* Sentinel */
 };
 
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
@@ -55,6 +55,9 @@
         raises(SystemError, "obj.broken_member = 42")
         assert module.fooType.broken_member.__doc__ is None
         assert module.fooType.object_member.__doc__ == "A Python object."
+        module.add_docstring(module.fooType.docless_member, "docstring for docless_member")
+        assert module.fooType.docless_member.__doc__ ==  "docstring for docless_member"
+        assert str(type(module.fooType.int_member)) == "<type 'member_descriptor'>"
 
     def test_typeobject_object_member(self):
         module = self.import_module(name='foo')


More information about the pypy-commit mailing list