[pypy-svn] pypy pyarg-parsebuffer-new: Merge the first pyarg branch
exarkun
commits-noreply at bitbucket.org
Tue Apr 19 22:01:03 CEST 2011
Author: Jean-Paul Calderone <exarkun at twistedmatrix.com>
Branch: pyarg-parsebuffer-new
Changeset: r43491:d834b9e5b238
Date: 2011-04-19 16:00 -0400
http://bitbucket.org/pypy/pypy/changeset/d834b9e5b238/
Log: Merge the first pyarg branch
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -16,9 +16,6 @@
*/
#define staticforward static
-typedef void* Py_buffer;
-
-
#define PyObject_HEAD \
long ob_refcnt; \
struct _typeobject *ob_type;
@@ -130,6 +127,29 @@
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 *);
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -411,6 +411,23 @@
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):
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -1,7 +1,8 @@
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,
@@ -428,3 +429,17 @@
rffi.free_nonmovingbuffer(data, buf)
return 0
+
+ at 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
+
+
+ at cpython_api([lltype.Ptr(Py_buffer)], lltype.Void, error=CANNOT_FAIL)
+def PyBuffer_Release(space, view):
+ pass
diff --git a/pypy/module/cpyext/src/getargs.c b/pypy/module/cpyext/src/getargs.c
--- a/pypy/module/cpyext/src/getargs.c
+++ b/pypy/module/cpyext/src/getargs.c
@@ -135,13 +135,11 @@
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 *))
@@ -776,15 +774,19 @@
}
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);
@@ -801,13 +803,13 @@
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;
@@ -1266,24 +1268,28 @@
}
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",
@@ -1293,16 +1299,23 @@
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);
@@ -1616,7 +1629,7 @@
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);
}
diff --git a/.hgsubstate b/.hgsubstate
new file mode 100644
diff --git a/pypy/module/cpyext/test/test_getargs.py b/pypy/module/cpyext/test/test_getargs.py
--- a/pypy/module/cpyext/test/test_getargs.py
+++ b/pypy/module/cpyext/test/test_getargs.py
@@ -3,66 +3,142 @@
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')
diff --git a/pypy/module/cpyext/test/test_object.py b/pypy/module/cpyext/test/test_object.py
--- a/pypy/module/cpyext/test/test_object.py
+++ b/pypy/module/cpyext/test/test_object.py
@@ -231,3 +231,107 @@
""")])
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
More information about the Pypy-commit
mailing list