[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