Merged revisions 65654 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r65654 | martin.v.loewis | 2008-08-12 16:49:50 +0200 (Tue, 12 Aug 2008) | 6 lines

  Issue #3139: Make buffer-interface thread-safe wrt. PyArg_ParseTuple,
  by denying s# to parse objects that have a releasebuffer procedure,
  and introducing s*.

  More module might need to get converted to use s*.
........
diff --git a/Python/getargs.c b/Python/getargs.c
index 36a6261..cf4444c 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -44,6 +44,7 @@
 static char *convertsimple(PyObject *, const char **, va_list *, int, char *,
 			   size_t, PyObject **);
 static Py_ssize_t convertbuffer(PyObject *, void **p, char **);
+static int getbuffer(PyObject *, Py_buffer *, char**);
 
 static int vgetargskeywords(PyObject *, PyObject *,
 			    const char *, char **, va_list *, int);
@@ -789,7 +790,25 @@
 	   need to be cleaned up! */
 
 	case 's': {/* text string */
-		if (*format == '#') {
+		if (*format == '*') {
+			Py_buffer *p = (Py_buffer *)va_arg(*p_va, Py_buffer *);
+
+			if (PyUnicode_Check(arg)) {
+				uarg = UNICODE_DEFAULT_ENCODING(arg);
+				if (uarg == NULL)
+					return converterr(CONV_UNICODE,
+							  arg, msgbuf, bufsize);
+				PyBuffer_FillInfo(p, arg,
+						  PyBytes_AS_STRING(uarg), PyBytes_GET_SIZE(uarg),
+						  1, 0);
+			}
+			else { /* any buffer-like object */
+				char *buf;
+				if (getbuffer(arg, p, &buf) < 0)
+					return converterr(buf, arg, msgbuf, bufsize);
+			}
+			format++;
+		} else if (*format == '#') {
 			void **p = (void **)va_arg(*p_va, char **);
 			FETCH_SIZE;
 
@@ -832,10 +851,17 @@
 	case 'y': {/* any buffer-like object, but not PyUnicode */
 		void **p = (void **)va_arg(*p_va, char **);
 		char *buf;
-		Py_ssize_t count = convertbuffer(arg, p, &buf);
+		Py_ssize_t count;
+		if (*format == '*') {
+			if (getbuffer(arg, (Py_buffer*)p, &buf) < 0)
+				return converterr(buf, arg, msgbuf, bufsize);
+			format++;
+			break;
+		}
+		count = convertbuffer(arg, p, &buf);
 		if (count < 0)
 			return converterr(buf, arg, msgbuf, bufsize);
-		if (*format == '#') {
+		else if (*format == '#') {
 			FETCH_SIZE;
 			STORE_SIZE(count);
 			format++;
@@ -844,7 +870,27 @@
 	}
 
 	case 'z': {/* like 's' or 's#', but None is okay, stored as NULL */
-		if (*format == '#') { /* any buffer-like object */
+		if (*format == '*') {
+			Py_buffer *p = (Py_buffer *)va_arg(*p_va, Py_buffer *);
+
+			if (arg == Py_None)
+				PyBuffer_FillInfo(p, NULL, NULL, 0, 1, 0);
+			else if (PyUnicode_Check(arg)) {
+				uarg = UNICODE_DEFAULT_ENCODING(arg);
+				if (uarg == NULL)
+					return converterr(CONV_UNICODE,
+							  arg, msgbuf, bufsize);
+				PyBuffer_FillInfo(p, arg,
+						  PyBytes_AS_STRING(uarg), PyBytes_GET_SIZE(uarg),
+						  1, 0);
+			}
+			else { /* any buffer-like object */
+				char *buf;
+				if (getbuffer(arg, p, &buf) < 0)
+					return converterr(buf, arg, msgbuf, bufsize);
+			}
+			format++;
+		} else if (*format == '#') { /* any buffer-like object */
 			void **p = (void **)va_arg(*p_va, char **);
 			FETCH_SIZE;
 
@@ -1189,6 +1235,26 @@
                 int temp=-1;
                 Py_buffer view;
 
+		if (pb && pb->bf_releasebuffer && *format != '*')
+			/* Buffer must be released, yet caller does not use
+			   the Py_buffer protocol. */
+			return converterr("pinned buffer", arg, msgbuf, bufsize);
+
+
+		if (pb && pb->bf_getbuffer && *format == '*') {
+			/* Caller is interested in Py_buffer, and the object
+			   supports it directly. */
+			format++;
+			if (pb->bf_getbuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) {
+				PyErr_Clear();
+				return converterr("read-write buffer", arg, msgbuf, bufsize);
+			}
+			if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C'))
+				return converterr("contiguous buffer", arg, msgbuf, bufsize);
+			break;
+		}
+
+		/* Here we have processed w*, only w and w# remain. */
 		if (pb == NULL ||
 		    pb->bf_getbuffer == NULL ||
                     ((temp = (*pb->bf_getbuffer)(arg, &view,
@@ -1209,8 +1275,6 @@
 			STORE_SIZE(count);
 			format++;
 		}
-                if (pb->bf_releasebuffer != NULL)
-                        (*pb->bf_releasebuffer)(arg, &view);
 		break;
 	}
 
@@ -1237,10 +1301,11 @@
 
                 count = view.len;
                 *p = view.buf;
-                /* XXX : shouldn't really release buffer, but it should be O.K.
-                */
-                if (pb->bf_releasebuffer != NULL)
-                        (*pb->bf_releasebuffer)(arg, &view);
+		if (pb->bf_releasebuffer)
+			return converterr(
+				"string or pinned buffer",
+				arg, msgbuf, bufsize);
+
 		if (count < 0)
 			return converterr("(unspecified)", arg, msgbuf, bufsize);
 		{
@@ -1269,7 +1334,8 @@
         *errmsg = NULL;
         *p = NULL;
 	if (pb == NULL ||
-	    pb->bf_getbuffer == NULL) {
+	    pb->bf_getbuffer == NULL ||
+	    pb->bf_releasebuffer != NULL) {
 		*errmsg = "bytes or read-only buffer";
 		return -1;
 	}
@@ -1285,6 +1351,35 @@
 	return count;
 }
 
+/* XXX for 3.x, getbuffer and convertbuffer can probably
+   be merged again. */
+static int
+getbuffer(PyObject *arg, Py_buffer *view, char**errmsg)
+{
+	void *buf;
+	Py_ssize_t count;
+	PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
+	if (pb == NULL) {
+		*errmsg = "string or buffer";
+		return -1;
+	}
+	if (pb->bf_getbuffer) {
+		if (pb->bf_getbuffer(arg, view, 0) < 0)
+			return -1;
+		if (!PyBuffer_IsContiguous(view, 'C')) {
+			*errmsg = "contiguous buffer";
+			return -1;
+		}
+		return 0;
+	}
+
+	count = convertbuffer(arg, &buf, errmsg);
+	if (count < 0)
+		return count;
+	PyBuffer_FillInfo(view, NULL, buf, count, 1, 0);
+	return 0;
+}
+
 /* Support for keyword arguments donated by
    Geoff Philbrick <philbric@delphi.hks.com> */
 
@@ -1624,6 +1719,8 @@
 				else
 					(void) va_arg(*p_va, int *);
 				format++;
+			} else if ((c == 's' || c == 'z' || c == 'y') && *format == '*') {
+				format++;
 			}
 			break;
 		}