Commits

Jean-Paul Calderone committed d834b9e Merge

Merge the first pyarg branch

Comments (0)

Files changed (7)

.hgsubstate

Empty file added.

pypy/module/cpyext/api.py

 PyVarObjectStruct = cpython_struct("PyVarObject", PyVarObjectFields)
 PyVarObject = lltype.Ptr(PyVarObjectStruct)
 
+Py_buffer = cpython_struct(
+    "Py_buffer", (
+        ('buf', rffi.VOIDP),
+        ('obj', PyObject),
+        ('len', Py_ssize_t),
+        # ('itemsize', Py_ssize_t),
+
+        # ('readonly', lltype.Signed),
+        # ('ndim', lltype.Signed),
+        # ('format', rffi.CCHARP),
+        # ('shape', Py_ssize_tP),
+        # ('strides', Py_ssize_tP),
+        # ('suboffets', Py_ssize_tP),
+        # ('smalltable', rffi.CFixedArray(Py_ssize_t, 2)),
+        # ('internal', rffi.VOIDP)
+        ))
+
 @specialize.memo()
 def is_PyObject(TYPE):
     if not isinstance(TYPE, lltype.Ptr):

pypy/module/cpyext/include/object.h

 */
 #define staticforward static
 
-typedef void* Py_buffer;
-
-
 #define PyObject_HEAD  \
     long ob_refcnt;       \
     struct _typeobject *ob_type;
 typedef int (*visitproc)(PyObject *, void *);
 typedef int (*traverseproc)(PyObject *, visitproc, void *);
 
+/* Py3k buffer interface */
+typedef struct bufferinfo {
+    void *buf;
+    PyObject *obj;        /* owned reference */
+    Py_ssize_t len;
+
+    /* This is Py_ssize_t so it can be
+       pointed to by strides in simple case.*/
+    /* Py_ssize_t itemsize; */
+    /* int readonly; */
+    /* int ndim; */
+    /* char *format; */
+    /* Py_ssize_t *shape; */
+    /* Py_ssize_t *strides; */
+    /* Py_ssize_t *suboffsets; */
+
+    /* static store for shape and strides of
+       mono-dimensional buffers. */
+    /* Py_ssize_t smalltable[2]; */
+    /* void *internal; */
+} Py_buffer;
+
+
 typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
 typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
 

pypy/module/cpyext/object.py

 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (
     cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t, Py_ssize_tP,
-    PyVarObject, Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
+    PyVarObject, Py_buffer,
+    Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
     Py_GE, CONST_STRING, FILEP, fwrite)
 from pypy.module.cpyext.pyobject import (
     PyObject, PyObjectP, create_ref, from_ref, Py_IncRef, Py_DecRef,
         rffi.free_nonmovingbuffer(data, buf)
     return 0
 
+
+@cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t,
+              lltype.Signed, lltype.Signed], rffi.INT, error=CANNOT_FAIL)
+def PyBuffer_FillInfo(space, view, obj, buf, length, readonly, flags):
+    view.c_buf = buf
+    view.c_len = length
+    view.c_obj = obj
+    Py_IncRef(space, obj)
+    return 0
+
+
+@cpython_api([lltype.Ptr(Py_buffer)], lltype.Void, error=CANNOT_FAIL)
+def PyBuffer_Release(space, view):
+    pass

pypy/module/cpyext/src/getargs.c

 	PyMem_FREE(ptr);
 }
 
-#if 0
 static void
 cleanup_buffer(void *ptr)
 {
 	PyBuffer_Release((Py_buffer *) ptr);
 }
-#endif
 
 static int
 addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *))
 	}
 	case 's': {/* string */
 		if (*format == '*') {
-      Py_FatalError("* format unsupported for strings in PyArg_*\n");
-#if 0
 			Py_buffer *p = (Py_buffer *)va_arg(*p_va, Py_buffer *);
 
 			if (PyString_Check(arg)) {
 				PyBuffer_FillInfo(p, arg,
 						  PyString_AS_STRING(arg), PyString_GET_SIZE(arg),
 						  1, 0);
-			}
+			} else {
+                            PyErr_SetString(
+                                PyExc_NotImplementedError,
+                                "s* not implemented for non-string values");
+                            return NULL;
+                        }
+#if 0
 #ifdef Py_USING_UNICODE
 			else if (PyUnicode_Check(arg)) {
 				uarg = UNICODE_DEFAULT_ENCODING(arg);
 				if (getbuffer(arg, p, &buf) < 0)
 					return converterr(buf, arg, msgbuf, bufsize);
 			}
+#endif
 			if (addcleanup(p, freelist, cleanup_buffer)) {
 				return converterr(
 					"(cleanup problem)",
 					arg, msgbuf, bufsize);
 			}
 			format++;
-#endif
 		} else if (*format == '#') {
 			void **p = (void **)va_arg(*p_va, char **);
 			FETCH_SIZE;
 	}
 		
 	case 't': { /* 8-bit character buffer, read-only access */
-    Py_FatalError("'t' unsupported");
-#if 0
 		char **p = va_arg(*p_va, char **);
 		PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
 		Py_ssize_t count;
-		
+                printf("this far\n");
+
+#if 0
 		if (*format++ != '#')
 			return converterr(
 				"invalid use of 't' format character", 
 				arg, msgbuf, bufsize);
+#endif
 		if (!PyType_HasFeature(arg->ob_type,
-				       Py_TPFLAGS_HAVE_GETCHARBUFFER) ||
-		    pb == NULL || pb->bf_getcharbuffer == NULL ||
-		    pb->bf_getsegcount == NULL)
+				       Py_TPFLAGS_HAVE_GETCHARBUFFER)
+#if 0
+		    || pb == NULL || pb->bf_getcharbuffer == NULL ||
+		    pb->bf_getsegcount == NULL
+#endif
+                    )
 			return converterr(
 				"string or read-only character buffer",
 				arg, msgbuf, bufsize);
-
+#if 0
 		if (pb->bf_getsegcount(arg, NULL) != 1)
 			return converterr(
 				"string or single-segment read-only buffer",
 			return converterr(
 				"string or pinned buffer",
 				arg, msgbuf, bufsize);
-
+#endif
+                printf("this far!\n");
+                printf("%p\n", pb->bf_getcharbuffer);
 		count = pb->bf_getcharbuffer(arg, 0, p);
+                printf("after\n");
+#if 0
 		if (count < 0)
 			return converterr("(unspecified)", arg, msgbuf, bufsize);
+#endif
 		{
+                    printf("fetch size\n");
 			FETCH_SIZE;
+                        printf("did that\n");
 			STORE_SIZE(count);
+                        printf("store size done\n");
 		}
 		break;
-#endif
 	}
 	default:
 		return converterr("impossible<bad format char>", arg, msgbuf, bufsize);
 			int match = 0;
 			char *ks;
 			if (!PyString_Check(key)) {
-				PyErr_SetString(PyExc_TypeError, 
+                            PyErr_SetString(PyExc_TypeError, 
 					        "keywords must be strings");
 				return cleanreturn(0, freelist);
 			}

pypy/module/cpyext/test/test_getargs.py

 from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
 
 class AppTestGetargs(AppTestCpythonExtensionBase):
-    def test_pyarg_parse(self):
-        mod = self.import_extension('foo', [
-            ('oneargint', 'METH_VARARGS',
-             '''
-             int l;
-             if (!PyArg_ParseTuple(args, "i", &l)) {
-                 return NULL;
-             }
-             return PyInt_FromLong(l);
-             '''
-             ),
-            ('oneargandform', 'METH_VARARGS',
-             '''
-             int l;
-             if (!PyArg_ParseTuple(args, "i:oneargandstuff", &l)) {
-                 return NULL;
-             }
-             return PyInt_FromLong(l);
-             '''),
-            ('oneargobject', 'METH_VARARGS',
-             '''
-             PyObject *obj;
-             if (!PyArg_ParseTuple(args, "O", &obj)) {
-                 return NULL;
-             }
-             Py_INCREF(obj);
-             return obj;
-             '''),
-            ('oneargobjectandlisttype', 'METH_VARARGS',
-             '''
-             PyObject *obj;
-             if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &obj)) {
-                 return NULL;
-             }
-             Py_INCREF(obj);
-             return obj;
-             '''),
-            ('twoopt', 'METH_VARARGS',
-             '''
-             PyObject *a;
-             PyObject *b = NULL;
-             if (!PyArg_ParseTuple(args, "O|O", &a, &b)) {
-                 return NULL;
-             }
-             if (b)
-                 Py_INCREF(b);
-             else
-                 b = PyInt_FromLong(42);
-             /* return an owned reference */
-             return b;
-             ''')])
-        assert mod.oneargint(1) == 1
-        raises(TypeError, mod.oneargint, None)
-        raises(TypeError, mod.oneargint)
-        assert mod.oneargandform(1) == 1
+    def setup_method(self, func):
+        super(AppTestGetargs, self).setup_method(func)
+        self.w_import_parser = self.space.wrap(self.import_parser)
 
+
+    def import_parser(self, implementation, argstyle='METH_VARARGS'):
+        mod = self.import_extension(
+            'modname', [('funcname', argstyle, implementation)])
+        return self.space.getattr(mod, self.space.wrap("funcname"))
+
+
+    def test_pyarg_parse_int(self):
+        """
+        The `i` format specifier can be used to parse an integer.
+        """
+        oneargint = self.import_parser(
+            '''
+            int l;
+            if (!PyArg_ParseTuple(args, "i", &l)) {
+                return NULL;
+            }
+            return PyInt_FromLong(l);
+            ''')
+        assert oneargint(1) == 1
+        raises(TypeError, oneargint, None)
+        raises(TypeError, oneargint)
+
+
+    def test_pyarg_parse_fromname(self):
+        """
+        The name of the function parsing the arguments can be given after a `:`
+        in the argument format string.
+        """
+        oneargandform = self.import_parser(
+            '''
+            int l;
+            if (!PyArg_ParseTuple(args, "i:oneargandstuff", &l)) {
+                return NULL;
+            }
+            return PyInt_FromLong(l);
+            ''')
+        assert oneargandform(1) == 1
+
+
+    def test_pyarg_parse_object(self):
+        """
+        The `O` format specifier can be used to parse an arbitrary object.
+        """
+        oneargobject = self.import_parser(
+            '''
+            PyObject *obj;
+            if (!PyArg_ParseTuple(args, "O", &obj)) {
+                return NULL;
+            }
+            Py_INCREF(obj);
+            return obj;
+            ''')
         sentinel = object()
-        res = mod.oneargobject(sentinel)
-        raises(TypeError, "mod.oneargobjectandlisttype(sentinel)")
+        res = oneargobject(sentinel)
         assert res is sentinel
-        assert mod.twoopt(1) == 42
-        assert mod.twoopt(1, 2) == 2
-        raises(TypeError, mod.twoopt, 1, 2, 3)
+
+    def test_pyarg_parse_restricted_object_type(self):
+        """
+        The `O!` format specifier can be used to parse an object of a particular
+        type.
+        """
+        oneargobjectandlisttype = self.import_parser(
+            '''
+            PyObject *obj;
+            if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &obj)) {
+                return NULL;
+            }
+            Py_INCREF(obj);
+            return obj;
+            ''')
+        sentinel = object()
+        raises(TypeError, "oneargobjectandlisttype(sentinel)")
+        sentinel = []
+        res = oneargobjectandlisttype(sentinel)
+        assert res is sentinel
+
+
+    def test_pyarg_parse_one_optional(self):
+        """
+        An object corresponding to a format specifier after a `|` in the
+        argument format string is optional and may be passed or not.
+        """
+        twoopt = self.import_parser(
+            '''
+            PyObject *a;
+            PyObject *b = NULL;
+            if (!PyArg_ParseTuple(args, "O|O", &a, &b)) {
+                return NULL;
+            }
+            if (b)
+                Py_INCREF(b);
+            else
+                b = PyInt_FromLong(42);
+            /* return an owned reference */
+            return b;
+            ''')
+        assert twoopt(1) == 42
+        assert twoopt(1, 2) == 2
+        raises(TypeError, twoopt, 1, 2, 3)
+
+
+    def test_pyarg_parse_string_py_buffer(self):
+        """
+        The `s*` format specifier can be used to parse a str into a Py_buffer
+        structure containing a pointer to the string data and the length of the
+        string data.
+        """
+        pybuffer = self.import_parser(
+            '''
+            Py_buffer buf;
+            if (!PyArg_ParseTuple(args, "s*", &buf)) {
+                return NULL;
+            }
+            return PyString_FromStringAndSize(buf.buf, buf.len);
+            ''')
+        assert 'foo\0bar\0baz' == pybuffer('foo\0bar\0baz')
+
+
+    def test_pyarg_parse_charbuf_and_length(self):
+        """
+        The `t#` format specifier can be used to parse a read-only 8-bit
+        character buffer into a char* and int giving its length in bytes.
+        """
+        charbuf = self.import_parser(
+            '''
+            char *buf;
+            int len;
+            if (!PyArg_ParseTuple(args, "t#", &buf, &len)) {
+                return NULL;
+            }
+            return PyString_FromStringAndSize(buf, len);
+            ''')
+        raises(TypeError, "charbuf(10)")
+        assert 'foo\0bar\0baz' == charbuf('foo\0bar\0baz')

pypy/module/cpyext/test/test_object.py

              """)])
         assert module.dump(self.tmpname, None)
         assert open(self.tmpname).read() == 'None'
+
+
+
+class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase):
+    """
+    PyBuffer_FillInfo populates the fields of a Py_buffer from its arguments.
+    """
+    def test_fillWithoutObject(self):
+        """
+        PyBuffer_FillInfo populates the C{buf} and C{length}fields of the
+        Py_buffer passed to it.
+        """
+        module = self.import_extension('foo', [
+                ("fillinfo", "METH_VARARGS",
+                 """
+    Py_buffer buf;
+    PyObject *str = PyString_FromString("hello, world.");
+    PyObject *result;
+
+    if (PyBuffer_FillInfo(&buf, NULL, PyString_AsString(str), 13, 0, 0)) {
+        return NULL;
+    }
+
+    /* Check a few things we want to have happened.
+     */
+    if (buf.buf != PyString_AsString(str)) {
+        PyErr_SetString(PyExc_ValueError, "buf field not initialized");
+        return NULL;
+    }
+
+    if (buf.len != 13) {
+        PyErr_SetString(PyExc_ValueError, "len field not initialized");
+        return NULL;
+    }
+
+    if (buf.obj != NULL) {
+        PyErr_SetString(PyExc_ValueError, "obj field not initialized");
+        return NULL;
+    }
+
+    /* Give back a new string to the caller, constructed from data in the
+     * Py_buffer.
+     */
+    if (!(result = PyString_FromStringAndSize(buf.buf, buf.len))) {
+        return NULL;
+    }
+
+    /* Free that string we allocated above.  result does not share storage with
+     * it.
+     */
+    Py_DECREF(str);
+
+    return result;
+                 """)])
+        result = module.fillinfo()
+        assert "hello, world." == result
+
+
+    def test_fillWithObject(self):
+        """
+        PyBuffer_FillInfo populates the C{buf}, C{length}, and C{obj} fields of
+        the Py_buffer passed to it and increments the reference count of the
+        object.
+        """
+        module = self.import_extension('foo', [
+                ("fillinfo", "METH_VARARGS",
+                 """
+    Py_buffer buf;
+    PyObject *str = PyString_FromString("hello, world.");
+    PyObject *result;
+
+    if (PyBuffer_FillInfo(&buf, str, PyString_AsString(str), 13, 0, 0)) {
+        return NULL;
+    }
+
+    /* Get rid of our own reference to the object, but the Py_buffer should
+     * still have a reference.
+     */
+    Py_DECREF(str);
+
+    /* Give back a new string to the caller, constructed from data in the
+     * Py_buffer.  It better still be valid.
+     */
+    if (!(result = PyString_FromStringAndSize(buf.buf, buf.len))) {
+        return NULL;
+    }
+
+    /* Now the data in the Py_buffer is really no longer needed, get rid of it
+     *(could use PyBuffer_Release here, but that would drag in more code than
+     * necessary).
+     */
+    Py_DECREF(buf.obj);
+
+    /* Py_DECREF can't directly signal error to us, but if it makes a reference
+     * count go negative, it will set an error.
+     */
+    if (PyErr_Occurred()) {
+        return NULL;
+    }
+
+    return result;
+                 """)])
+        result = module.fillinfo()
+        assert "hello, world." == result