[Python-checkins] r46159 - in python/branches/blais-bytebuf: Lib/socket.py Lib/test/test_socket.py Modules/arraymodule.c Modules/bytebufmodule.c Modules/socketmodule.c

martin.blais python-checkins at python.org
Wed May 24 12:11:51 CEST 2006


Author: martin.blais
Date: Wed May 24 12:11:49 2006
New Revision: 46159

Modified:
   python/branches/blais-bytebuf/Lib/socket.py
   python/branches/blais-bytebuf/Lib/test/test_socket.py
   python/branches/blais-bytebuf/Modules/arraymodule.c
   python/branches/blais-bytebuf/Modules/bytebufmodule.c
   python/branches/blais-bytebuf/Modules/socketmodule.c
Log:
Implemented socket.socket.recv_buf

Modified: python/branches/blais-bytebuf/Lib/socket.py
==============================================================================
--- python/branches/blais-bytebuf/Lib/socket.py	(original)
+++ python/branches/blais-bytebuf/Lib/socket.py	Wed May 24 12:11:49 2006
@@ -140,7 +140,7 @@
 
     __doc__ = _realsocket.__doc__
 
-    __slots__ = ["_sock", "send", "recv", "sendto", "recvfrom",
+    __slots__ = ["_sock", "send", "recv", "recv_buf", "sendto", "recvfrom",
                  "__weakref__"]
 
     def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
@@ -149,6 +149,7 @@
         self._sock = _sock
         self.send = self._sock.send
         self.recv = self._sock.recv
+        self.recv_buf = self._sock.recv_buf
         self.sendto = self._sock.sendto
         self.recvfrom = self._sock.recvfrom
 

Modified: python/branches/blais-bytebuf/Lib/test/test_socket.py
==============================================================================
--- python/branches/blais-bytebuf/Lib/test/test_socket.py	(original)
+++ python/branches/blais-bytebuf/Lib/test/test_socket.py	Wed May 24 12:11:49 2006
@@ -9,6 +9,8 @@
 import thread, threading
 import Queue
 import sys
+import array
+import bytebuf
 from weakref import proxy
 
 PORT = 50007
@@ -852,6 +854,103 @@
         self.assertRaises(socket.error, s.bind, address)
 
 
+class BufferIOTest(SocketConnectedTest):
+    """
+    Test the buffer versions of socket.recv() and socket.send().
+    """
+    def __init__(self, methodName='runTest'):
+        SocketConnectedTest.__init__(self, methodName=methodName)
+
+    def testRecv(self):
+        # Receive into the buffer of an array class.
+        buf = array.array('c', ' '*1024)
+        nbytes = self.cli_conn.recv_buf(buf)
+        self.assertEqual(nbytes, len(MSG))
+        trunc = buf.tostring()[:len(MSG)]
+        self.assertEqual(trunc, MSG)
+
+    def _testRecv(self):
+        # Send using a read-only buffer.
+        buf = buffer(MSG)
+        self.serv_conn.send(buf)
+
+    def testRecvBB(self):
+        # Receive into the buffer of an array class.
+        buf = bytebuf.bytebuf(1024)
+        nbytes = self.cli_conn.recv_buf(buf)
+        self.assertEqual(nbytes, len(MSG))
+        trunc = str(buf)[:len(MSG)]
+        self.assertEqual(trunc, MSG)
+
+    def _testRecvBB(self):
+        # Send using a read-only buffer.
+##         buf = bytebuf.bytebuf(MSG)
+        self.serv_conn.send(MSG)
+
+##     def testOverFlowRecv(self):
+##         # Testing receive in chunks over TCP
+##         seg1 = self.cli_conn.recv(len(MSG) - 3)
+##         seg2 = self.cli_conn.recv(1024)
+##         msg = seg1 + seg2
+##         self.assertEqual(msg, MSG)
+
+##     def _testOverFlowRecv(self):
+##         self.serv_conn.send(MSG)
+
+##     def testRecvFrom(self):
+##         # Testing large recvfrom() over TCP
+##         msg, addr = self.cli_conn.recvfrom(1024)
+##         self.assertEqual(msg, MSG)
+
+##     def _testRecvFrom(self):
+##         self.serv_conn.send(MSG)
+
+##     def testOverFlowRecvFrom(self):
+##         # Testing recvfrom() in chunks over TCP
+##         seg1, addr = self.cli_conn.recvfrom(len(MSG)-3)
+##         seg2, addr = self.cli_conn.recvfrom(1024)
+##         msg = seg1 + seg2
+##         self.assertEqual(msg, MSG)
+
+##     def _testOverFlowRecvFrom(self):
+##         self.serv_conn.send(MSG)
+
+##     def testSendAll(self):
+##         # Testing sendall() with a 2048 byte string over TCP
+##         msg = ''
+##         while 1:
+##             read = self.cli_conn.recv(1024)
+##             if not read:
+##                 break
+##             msg += read
+##         self.assertEqual(msg, 'f' * 2048)
+
+##     def _testSendAll(self):
+##         big_chunk = 'f' * 2048
+##         self.serv_conn.sendall(big_chunk)
+
+##     def testFromFd(self):
+##         # Testing fromfd()
+##         if not hasattr(socket, "fromfd"):
+##             return # On Windows, this doesn't exist
+##         fd = self.cli_conn.fileno()
+##         sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
+##         msg = sock.recv(1024)
+##         self.assertEqual(msg, MSG)
+
+##     def _testFromFd(self):
+##         self.serv_conn.send(MSG)
+
+##     def testShutdown(self):
+##         # Testing shutdown()
+##         msg = self.cli_conn.recv(1024)
+##         self.assertEqual(msg, MSG)
+
+##     def _testShutdown(self):
+##         self.serv_conn.send(MSG)
+##         self.serv_conn.shutdown(2)
+
+
 def test_main():
     tests = [GeneralModuleTests, BasicTCPTest, TCPTimeoutTest, TestExceptions]
     if sys.platform != 'mac':
@@ -870,5 +969,9 @@
         tests.append(TestLinuxAbstractNamespace)
     test_support.run_unittest(*tests)
 
+def test_main2():
+    tests = [BufferIOTest]
+    test_support.run_unittest(*tests)
+
 if __name__ == "__main__":
-    test_main()
+    test_main2()

Modified: python/branches/blais-bytebuf/Modules/arraymodule.c
==============================================================================
--- python/branches/blais-bytebuf/Modules/arraymodule.c	(original)
+++ python/branches/blais-bytebuf/Modules/arraymodule.c	Wed May 24 12:11:49 2006
@@ -1975,9 +1975,9 @@
 	0,					/* tp_setattr */
 	0,					/* tp_compare */
 	(reprfunc)array_repr,			/* tp_repr */
-	0,					/* tp_as _number*/
-	&array_as_sequence,			/* tp_as _sequence*/
-	&array_as_mapping,			/* tp_as _mapping*/
+	0,					/* tp_as_number*/
+	&array_as_sequence,			/* tp_as_sequence*/
+	&array_as_mapping,			/* tp_as_mapping*/
 	0, 					/* tp_hash */
 	0,					/* tp_call */
 	0,					/* tp_str */

Modified: python/branches/blais-bytebuf/Modules/bytebufmodule.c
==============================================================================
--- python/branches/blais-bytebuf/Modules/bytebufmodule.c	(original)
+++ python/branches/blais-bytebuf/Modules/bytebufmodule.c	Wed May 24 12:11:49 2006
@@ -1,4 +1,6 @@
-/* Bytebuf object interface */
+/* ===========================================================================
+ * Bytebuf object
+ */
 
 #include "Python.h"
 
@@ -25,7 +27,10 @@
 #endif /* !Py_BYTEBUFOBJECT_H */
 
 
-/* Byte Buffer object implementation */
+/* ===========================================================================
+ * Byte Buffer object implementation 
+ */
+
 
 /*
  * bytebuf object structure declaration.
@@ -47,6 +52,11 @@
  * Given a bytebuf object, return the buffer memory (in 'ptr' and 'size') and
  * true if there was no error.
  */
+
+
+/* FIXME remove get_buf() everywhere, at least the checks for the return
+   value. */
+
 static int
 get_buf(PyBytebufObject *self, void **ptr, Py_ssize_t *size)
 {
@@ -217,27 +227,25 @@
     return size;
 }
 
+/* ===========================================================================
+ * Sequence methods 
+ */
 
-PyDoc_STRVAR(module_doc,
-             "This module defines an object type which can represent a fixed size\n\
-buffer of bytes in momery, from which you can directly read and into\n\
-which you can directly write objects in various other types.  This is\n\
-used to avoid buffer copies in network I/O as much as possible.  For\n\
-example, socket recv() can directly fill a byte buffer's memory and\n\
-send() can read the data to be sent from one as well.\n\
-\n\
-In addition, a byte buffer has two pointers within it, that delimit\n\
-an active slice, the current \"position\" and the \"limit\".  The\n\
-active region of a byte buffer is located within these boundaries.\n\
-\n\
-This class is heaviliy inspired from Java's NIO ByteBuffer class.\n\
-\n\
-The constructor is:\n\
-\n\
-bytebuf(nbytes) -- create a new bytebuf\n\
-");
+static Py_ssize_t
+bytebuf_length(PyBytebufObject *self)
+{
+    void *ptr;
+    Py_ssize_t size;
+    if (!get_buf(self, &ptr, &size))
+        return -1;
+    return size;
+}
 
 
+/* ===========================================================================
+ * Object interfaces declaration 
+ */
+
 /* FIXME: needs an update */
 
 /* PyDoc_STRVAR(bytebuf_doc, */
@@ -260,6 +268,16 @@
 ");
 
 
+static PySequenceMethods bytebuf_as_sequence = {
+        (lenfunc)bytebuf_length,                 /*sq_length*/
+        0 /* (binaryfunc)bytebuf_concat */,              /*sq_concat*/
+        0 /* (ssizeargfunc)bytebuf_repeat */,            /*sq_repeat*/
+        0 /* (ssizeargfunc)bytebuf_item */,              /*sq_item*/
+        0 /*(ssizessizeargfunc)bytebuf_slice*/,        /*sq_slice*/
+        0 /*(ssizeobjargproc)bytebuf_ass_item*/,       /*sq_ass_item*/
+        0 /*(ssizessizeobjargproc)bytebuf_ass_slice*/, /*sq_ass_slice*/
+};
+
 static PyBufferProcs bytebuf_as_buffer = {
     (readbufferproc)bytebuf_getwritebuf,
     (writebufferproc)bytebuf_getwritebuf,
@@ -273,18 +291,18 @@
     "bytebuf",
     sizeof(PyBytebufObject),
     0,
-    (destructor)bytebuf_dealloc,                /* tp_dealloc */
+    (destructor)bytebuf_dealloc,        /* tp_dealloc */
     0,                                  /* tp_print */
     0,                                  /* tp_getattr */
     0,                                  /* tp_setattr */
     (cmpfunc)bytebuf_compare,           /* tp_compare */
-    (reprfunc)bytebuf_repr,                     /* tp_repr */
+    (reprfunc)bytebuf_repr,             /* tp_repr */
     0,                                  /* tp_as_number */
-    0,                                  /* tp_as_sequence */
+    &bytebuf_as_sequence,               /* tp_as_sequence */
     0,                                  /* tp_as_mapping */
     0,                                  /* tp_hash */
     0,                                  /* tp_call */
-    (reprfunc)bytebuf_str,                      /* tp_str */
+    (reprfunc)bytebuf_str,              /* tp_str */
     PyObject_GenericGetAttr,            /* tp_getattro */
     0,                                  /* tp_setattro */
     &bytebuf_as_buffer,                 /* tp_as_buffer */
@@ -306,11 +324,33 @@
     0,                                  /* tp_dictoffset */
     0,                                  /* tp_init */
     0,                                  /* tp_alloc */
-    bytebuf_new,                                /* tp_new */
+    bytebuf_new,                        /* tp_new */
 };
 
 
-/*********************** Install Module **************************/
+/* ===========================================================================
+ * Install Module
+ */
+
+PyDoc_STRVAR(module_doc,
+             "This module defines an object type which can represent a fixed size\n\
+buffer of bytes in momery, from which you can directly read and into\n\
+which you can directly write objects in various other types.  This is\n\
+used to avoid buffer copies in network I/O as much as possible.  For\n\
+example, socket recv() can directly fill a byte buffer's memory and\n\
+send() can read the data to be sent from one as well.\n\
+\n\
+In addition, a byte buffer has two pointers within it, that delimit\n\
+an active slice, the current \"position\" and the \"limit\".  The\n\
+active region of a byte buffer is located within these boundaries.\n\
+\n\
+This class is heaviliy inspired from Java's NIO ByteBuffer class.\n\
+\n\
+The constructor is:\n\
+\n\
+bytebuf(nbytes) -- create a new bytebuf\n\
+");
+
 
 /* No functions in array module. */
 static PyMethodDef a_methods[] = {

Modified: python/branches/blais-bytebuf/Modules/socketmodule.c
==============================================================================
--- python/branches/blais-bytebuf/Modules/socketmodule.c	(original)
+++ python/branches/blais-bytebuf/Modules/socketmodule.c	Wed May 24 12:11:49 2006
@@ -2135,95 +2135,126 @@
 
 #endif /* NO_DUP */
 
-
-/* s.recv(nbytes [,flags]) method */
-
-static PyObject *
-sock_recv(PySocketSockObject *s, PyObject *args)
+/*
+ * This is the guts of the recv() and recv_buf() methods, which reads into a
+ * char buffer.  If you have any inc/def ref to do to the objects that contain
+ * the buffer, do it in the caller.  This function returns the number of bytes
+ * succesfully read.  If there was an error, it returns -1.  Note that it is
+ * also possible that we return a number of bytes smaller than the request
+ * bytes.
+ */
+static int
+sock_recv_guts(PySocketSockObject *s, char* cbuf, int len, int flags)
 {
-	int len, n = 0, flags = 0, timeout;
-	PyObject *buf;
+        int timeout, outlen = 0;
 #ifdef __VMS
-	int read_length;
+	int remaining, nread;
 	char *read_buf;
 #endif
 
-	if (!PyArg_ParseTuple(args, "i|i:recv", &len, &flags))
-		return NULL;
-
-	if (len < 0) {
-		PyErr_SetString(PyExc_ValueError,
-				"negative buffersize in recv");
-		return NULL;
+	if (!IS_SELECTABLE(s)) {
+		select_error();
+		return -1;
 	}
 
-	buf = PyString_FromStringAndSize((char *) 0, len);
-	if (buf == NULL)
-		return NULL;
-
-	if (!IS_SELECTABLE(s))
-		return select_error();
-
 #ifndef __VMS
 	Py_BEGIN_ALLOW_THREADS
 	timeout = internal_select(s, 0);
 	if (!timeout)
-		n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags);
+		outlen = recv(s->sock_fd, cbuf, len, flags);
 	Py_END_ALLOW_THREADS
 
 	if (timeout) {
-		Py_DECREF(buf);
 		PyErr_SetString(socket_timeout, "timed out");
-		return NULL;
+		return -1;
 	}
-	if (n < 0) {
-		Py_DECREF(buf);
-		return s->errorhandler();
+	if (outlen < 0) {
+		/* Note: the call to errorhandler() ALWAYS indirectly returned
+		   NULL, so ignore its return value */
+		s->errorhandler();
+		return -1;
 	}
-	if (n != len)
-		_PyString_Resize(&buf, n);
 #else
-	read_buf = PyString_AsString(buf);
-	read_length = len;
-	while (read_length != 0) {
+	read_buf = cbuf;
+	remaining = len;
+	while (remaining != 0) {
 		unsigned int segment;
 
-		segment = read_length /SEGMENT_SIZE;
+		segment = remaining /SEGMENT_SIZE;
 		if (segment != 0) {
 			segment = SEGMENT_SIZE;
 		}
 		else {
-			segment = read_length;
+			segment = remaining;
 		}
 
 		Py_BEGIN_ALLOW_THREADS
 		timeout = internal_select(s, 0);
 		if (!timeout)
-			n = recv(s->sock_fd, read_buf, segment, flags);
+			nread = recv(s->sock_fd, read_buf, segment, flags);
 		Py_END_ALLOW_THREADS
 
 		if (timeout) {
-			Py_DECREF(buf);
 			PyErr_SetString(socket_timeout, "timed out");
-			return NULL;
+			return -1;
 		}
-		if (n < 0) {
-			Py_DECREF(buf);
-			return s->errorhandler();
+		if (nread < 0) {
+			s->errorhandler();
+			return -1;
 		}
-		if (n != read_length) {
-			read_buf += n;
+		if (nread != remaining) {
+			read_buf += nread;
 			break;
 		}
 
-		read_length -= segment;
+		remaining -= segment;
 		read_buf += segment;
 	}
-	if (_PyString_Resize(&buf, (read_buf - PyString_AsString(buf))) < 0)
-	{
-	    return NULL;
-	}
+	outlen = read_buf - cbuf;
 #endif /* !__VMS */
+
+	return outlen;
+}
+
+
+/* s.recv(nbytes [,flags]) method */
+
+static PyObject *
+sock_recv(PySocketSockObject *s, PyObject *args)
+{
+	int recvlen, flags = 0, outlen;
+	PyObject *buf;
+
+	if (!PyArg_ParseTuple(args, "i|i:recv", &recvlen, &flags))
+		return NULL;
+
+	if (recvlen < 0) {
+		PyErr_SetString(PyExc_ValueError,
+				"negative buffersize in recv");
+		return NULL;
+	}
+
+	/* Allocate a new string. */
+	buf = PyString_FromStringAndSize((char *) 0, recvlen);
+	if (buf == NULL)
+		return NULL;
+
+	/* Call the guts */
+	outlen = sock_recv_guts(s, PyString_AsString(buf), recvlen, flags);
+	if (outlen < 0) {
+		/* An error occured, release the string and return an
+		   error. */
+		Py_DECREF(buf);
+		return NULL;
+	}
+	if (outlen != recvlen) {
+		/* We did not read as many bytes as we anticipated, resize the
+		   string if possible and be succesful. */
+		if (_PyString_Resize(&buf, outlen) < 0)
+			/* Oopsy, not so succesful after all. */
+			return NULL;
+	}
+
 	return buf;
 }
 
@@ -2236,6 +2267,62 @@
 the remote end is closed and all data is read, return the empty string.");
 
 
+/* s.recv_buf(buffer, [nbytes [,flags]]) method */
+
+static PyObject*
+sock_recv_buf(PySocketSockObject *s, PyObject *args, PyObject *kwds)
+{
+	static char *kwlist[] = {"buffer", "nbytes", "flags", 0};
+
+	int recvlen = 0, flags = 0, readlen;
+	char *buf;
+	int buflen;
+
+	/* Get the buffer's memory */
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#|ii:recv", kwlist,
+					 &buf, &buflen, &recvlen, &flags))
+		return NULL;
+	assert(buf != 0 && buflen > 0);
+
+	if (recvlen < 0) {
+		PyErr_SetString(PyExc_ValueError,
+				"negative buffersize in recv");
+		return NULL;
+	}
+	if (recvlen == 0) {
+            /* If nbytes was not specified, use the buffer's length */
+            recvlen = buflen;
+	}
+
+	/* Check if the buffer is large enough */
+	if (buflen < recvlen) {
+		PyErr_SetString(PyExc_ValueError,
+				"buffer too small for requested bytes");
+		return NULL;
+	}
+
+	/* Call the guts */
+	readlen = sock_recv_guts(s, buf, recvlen, flags);
+	if (readlen < 0) {
+		/* Return an error. */
+		return NULL;
+	}
+
+	/* Return the number of bytes read.  Note that we do not do anything
+	   special here in the case that readlen < recvlen. */
+	return PyInt_FromLong(readlen);
+}
+
+PyDoc_STRVAR(recv_buf_doc,
+"recv_buf(buffer, [nbytes[, flags]]) -> nbytes_read\n\
+\n\
+A version of recv() that stores its data into a buffer rather than creating \n\
+a new string.  Receive up to buffersize bytes from the socket.  If buffersize \n\
+is not specified (or 0), receive up to the size available in the given buffer.\n\
+\n\
+See recv() for documentation about the flags.");
+
+
 /* s.recvfrom(nbytes [,flags]) method */
 
 static PyObject *
@@ -2535,6 +2622,8 @@
 #endif
 	{"recv",	(PyCFunction)sock_recv, METH_VARARGS,
 			recv_doc},
+	{"recv_buf",	(PyCFunction)sock_recv_buf, METH_VARARGS | METH_KEYWORDS,
+			recv_buf_doc},
 	{"recvfrom",	(PyCFunction)sock_recvfrom, METH_VARARGS,
 			recvfrom_doc},
 	{"send",	(PyCFunction)sock_send, METH_VARARGS,


More information about the Python-checkins mailing list