From 7d0a8264ff29fe8963bb1cc67debc03e7c728a98 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 21 May 2007 23:13:11 +0000 Subject: [PATCH] Sockets facelift. APIs that could return binary data (e.g. aton() and recv()) now return bytes, not str or str8. The socket.py code is redone; it now subclasses _socket.socket and instead of having its own _fileobject for makefile(), it uses io.SocketIO. Some stuff in io.py was moved around to make this work. (I really need to rethink my policy regarding readline() and read(-1) on raw files; and readline() on buffered files ought to use peeking(). Later.) --- Lib/io.py | 54 ++++-- Lib/socket.py | 395 ++++++++++------------------------------ Lib/test/test_socket.py | 55 ++---- Lib/urllib2.py | 4 +- Modules/socketmodule.c | 208 ++++++--------------- 5 files changed, 200 insertions(+), 516 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index 5f503c28429..9cbc11c22f7 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -295,6 +295,22 @@ class IOBase: """ return False + ### Readline ### + + def readline(self, sizehint: int = -1) -> bytes: + """For backwards compatibility, a (slow) readline().""" + if sizehint is None: + sizehint = -1 + res = b"" + while sizehint < 0 or len(res) < sizehint: + b = self.read(1) + if not b: + break + res += b + if b == b"\n": + break + return res + class RawIOBase(IOBase): @@ -366,7 +382,6 @@ class SocketIO(RawIOBase): """Raw I/O implementation for stream sockets.""" # XXX More docs - # XXX Hook this up to socket.py def __init__(self, sock, mode): assert mode in ("r", "w", "rw") @@ -377,13 +392,32 @@ class SocketIO(RawIOBase): def readinto(self, b): return self._sock.recv_into(b) + def read(self, n: int = None) -> bytes: + """read(n: int) -> bytes. Read and return up to n bytes. + + Returns an empty bytes array on EOF, or None if the object is + set not to block and has no data to read. + """ + if n is None: + n = -1 + if n >= 0: + return RawIOBase.read(self, n) + # Support reading until the end. + # XXX Why doesn't RawIOBase support this? + data = b"" + while True: + more = RawIOBase.read(self, DEFAULT_BUFFER_SIZE) + if not more: + break + data += more + return data + def write(self, b): return self._sock.send(b) def close(self): if not self.closed: - RawIOBase.close() - self._sock.close() + RawIOBase.close(self) def readable(self): return "r" in self._mode @@ -450,20 +484,6 @@ class BufferedIOBase(IOBase): b[:n] = data return n - def readline(self, sizehint: int = -1) -> bytes: - """For backwards compatibility, a (slow) readline().""" - if sizehint is None: - sizehint = -1 - res = b"" - while sizehint < 0 or len(res) < sizehint: - b = self.read(1) - if not b: - break - res += b - if b == b"\n": - break - return res - def write(self, b: bytes) -> int: """write(b: bytes) -> int. Write the given buffer to the IO stream. diff --git a/Lib/socket.py b/Lib/socket.py index 8dd23835d37..03cdc65f764 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -54,7 +54,7 @@ try: except ImportError: pass -import os, sys +import os, sys, io try: from errno import EBADF @@ -66,14 +66,6 @@ __all__.extend(os._get_exports_list(_socket)) if _have_ssl: __all__.extend(os._get_exports_list(_ssl)) -_realsocket = socket -if _have_ssl: - _realssl = ssl - def ssl(sock, keyfile=None, certfile=None): - if hasattr(sock, "_sock"): - sock = sock._sock - return _realssl(sock, keyfile, certfile) - # WSA error codes if sys.platform.lower().startswith("win"): errorTab = {} @@ -95,6 +87,99 @@ if sys.platform.lower().startswith("win"): __all__.append("errorTab") +_os_has_dup = hasattr(os, "dup") +if _os_has_dup: + def fromfd(fd, family=AF_INET, type=SOCK_STREAM, proto=0): + nfd = os.dup(fd) + return socket(family, type, proto, fileno=nfd) + + +class socket(_socket.socket): + + """A subclass of _socket.socket adding the makefile() method.""" + + __slots__ = ["__weakref__"] + if not _os_has_dup: + __slots__.append("_base") + + def __repr__(self): + """Wrap __repr__() to reveal the real class name.""" + s = _socket.socket.__repr__(self) + if s.startswith(" socket object - - Return a new socket object connected to the same system resource.""" - return _socketobject(_sock=self._sock) - - def makefile(self, mode='r', bufsize=-1): - """makefile([mode[, bufsize]]) -> file object - - Return a regular file object corresponding to the socket. The mode - and bufsize arguments are as for the built-in open() function.""" - return _fileobject(self._sock, mode, bufsize) - - family = property(lambda self: self._sock.family, doc="the socket family") - type = property(lambda self: self._sock.type, doc="the socket type") - proto = property(lambda self: self._sock.proto, doc="the socket protocol") - - _s = ("def %s(self, *args): return self._sock.%s(*args)\n\n" - "%s.__doc__ = _realsocket.%s.__doc__\n") - for _m in _socketmethods: - exec(_s % (_m, _m, _m, _m)) - del _m, _s - -socket = SocketType = _socketobject - -class _fileobject(object): - """Faux file object attached to a socket object.""" - - default_bufsize = 8192 - name = "" - - __slots__ = ["mode", "bufsize", - # "closed" is a property, see below - "_sock", "_rbufsize", "_wbufsize", "_rbuf", "_wbuf", - "_close"] - - def __init__(self, sock, mode='rb', bufsize=-1, close=False): - self._sock = sock - self.mode = mode # Not actually used in this version - if bufsize < 0: - bufsize = self.default_bufsize - self.bufsize = bufsize - if bufsize == 0: - self._rbufsize = 1 - elif bufsize == 1: - self._rbufsize = self.default_bufsize - else: - self._rbufsize = bufsize - self._wbufsize = bufsize - self._rbuf = "" # A string - self._wbuf = [] # A list of strings - self._close = close - - def _getclosed(self): - return self._sock is None - closed = property(_getclosed, doc="True if the file is closed") - - def close(self): - try: - if self._sock: - self.flush() - finally: - if self._close: - self._sock.close() - self._sock = None - - def __del__(self): - try: - self.close() - except: - # close() may fail if __init__ didn't complete - pass - - def flush(self): - if self._wbuf: - buffer = "".join(self._wbuf) - self._wbuf = [] - self._sock.sendall(buffer) - - def fileno(self): - return self._sock.fileno() - - def write(self, data): - data = str(data) # XXX Should really reject non-string non-buffers - if not data: - return - self._wbuf.append(data) - if (self._wbufsize == 0 or - self._wbufsize == 1 and '\n' in data or - self._get_wbuf_len() >= self._wbufsize): - self.flush() - - def writelines(self, list): - # XXX We could do better here for very long lists - # XXX Should really reject non-string non-buffers - self._wbuf.extend(filter(None, map(str, list))) - if (self._wbufsize <= 1 or - self._get_wbuf_len() >= self._wbufsize): - self.flush() - - def _get_wbuf_len(self): - buf_len = 0 - for x in self._wbuf: - buf_len += len(x) - return buf_len - - def read(self, size=-1): - data = self._rbuf - if size < 0: - # Read until EOF - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - if self._rbufsize <= 1: - recv_size = self.default_bufsize - else: - recv_size = self._rbufsize - while True: - data = self._sock.recv(recv_size) - if not data: - break - buffers.append(data) - return "".join(buffers) - else: - # Read until size bytes or EOF seen, whichever comes first - buf_len = len(data) - if buf_len >= size: - self._rbuf = data[size:] - return data[:size] - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - while True: - left = size - buf_len - recv_size = max(self._rbufsize, left) - data = self._sock.recv(recv_size) - if not data: - break - buffers.append(data) - n = len(data) - if n >= left: - self._rbuf = data[left:] - buffers[-1] = data[:left] - break - buf_len += n - return "".join(buffers) - - def readline(self, size=-1): - data = self._rbuf - if size < 0: - # Read until \n or EOF, whichever comes first - if self._rbufsize <= 1: - # Speed up unbuffered case - assert data == "" - buffers = [] - recv = self._sock.recv - while data != "\n": - data = recv(1) - if not data: - break - buffers.append(data) - return "".join(buffers) - nl = data.find('\n') - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - return data[:nl] - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - while True: - data = self._sock.recv(self._rbufsize) - if not data: - break - buffers.append(data) - nl = data.find('\n') - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - buffers[-1] = data[:nl] - break - return "".join(buffers) - else: - # Read until size bytes or \n or EOF seen, whichever comes first - nl = data.find('\n', 0, size) - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - return data[:nl] - buf_len = len(data) - if buf_len >= size: - self._rbuf = data[size:] - return data[:size] - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - while True: - data = self._sock.recv(self._rbufsize) - if not data: - break - buffers.append(data) - left = size - buf_len - nl = data.find('\n', 0, left) - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - buffers[-1] = data[:nl] - break - n = len(data) - if n >= left: - self._rbuf = data[left:] - buffers[-1] = data[:left] - break - buf_len += n - return "".join(buffers) - - def readlines(self, sizehint=0): - total = 0 - list = [] - while True: - line = self.readline() - if not line: - break - list.append(line) - total += len(line) - if sizehint and total >= sizehint: - break - return list - - # Iterator protocols - - def __iter__(self): - return self - - def __next__(self): - line = self.readline() - if not line: - raise StopIteration - return line - - def create_connection(address, timeout=None): """Connect to address (host, port) with an optional timeout. diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 350adedc3e1..51583786035 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -14,7 +14,7 @@ import signal PORT = 50007 HOST = 'localhost' -MSG = 'Michael Gilfix was here\n' +MSG = b'Michael Gilfix was here\n' class SocketTCPTest(unittest.TestCase): @@ -542,16 +542,16 @@ class BasicTCPTest(SocketConnectedTest): def testSendAll(self): # Testing sendall() with a 2048 byte string over TCP - msg = '' + msg = b'' while 1: read = self.cli_conn.recv(1024) if not read: break msg += read - self.assertEqual(msg, 'f' * 2048) + self.assertEqual(msg, b'f' * 2048) def _testSendAll(self): - big_chunk = 'f' * 2048 + big_chunk = b'f' * 2048 self.serv_conn.sendall(big_chunk) def testFromFd(self): @@ -612,7 +612,7 @@ class TCPCloserTest(ThreadedTCPSocketTest): sd = self.cli read, write, err = select.select([sd], [], [], 1.0) self.assertEqual(read, [sd]) - self.assertEqual(sd.recv(1), '') + self.assertEqual(sd.recv(1), b'') def _testClose(self): self.cli.connect((HOST, PORT)) @@ -754,7 +754,7 @@ class FileObjectClassTestCase(SocketConnectedTest): def testUnbufferedRead(self): # Performing unbuffered file read test - buf = '' + buf = b'' while 1: char = self.serv_file.read(1) if not char: @@ -796,14 +796,14 @@ class UnbufferedFileObjectClassTestCase(FileObjectClassTestCase): def testUnbufferedReadline(self): # Read a line, create a new file object, read another line with it line = self.serv_file.readline() # first line - self.assertEqual(line, "A. " + MSG) # first line + self.assertEqual(line, b"A. " + MSG) # first line self.serv_file = self.cli_conn.makefile('rb', 0) line = self.serv_file.readline() # second line - self.assertEqual(line, "B. " + MSG) # second line + self.assertEqual(line, b"B. " + MSG) # second line def _testUnbufferedReadline(self): - self.cli_file.write("A. " + MSG) - self.cli_file.write("B. " + MSG) + self.cli_file.write(b"A. " + MSG) + self.cli_file.write(b"B. " + MSG) self.cli_file.flush() class LineBufferedFileObjectClassTestCase(FileObjectClassTestCase): @@ -818,6 +818,7 @@ class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase): class NetworkConnectionTest(object): """Prove network connection.""" + def clientSetUp(self): self.cli = socket.create_connection((HOST, PORT)) self.serv_conn = self.cli @@ -827,6 +828,7 @@ class BasicTCPTest2(NetworkConnectionTest, BasicTCPTest): """ class NetworkConnectionNoServer(unittest.TestCase): + def testWithoutServer(self): self.failUnlessRaises(socket.error, lambda: socket.create_connection((HOST, PORT))) @@ -895,43 +897,19 @@ class NetworkConnectionBehaviourTest(SocketTCPTest, ThreadableTest): def testInsideTimeout(self): conn, addr = self.serv.accept() time.sleep(3) - conn.send("done!") + conn.send(b"done!") testOutsideTimeout = testInsideTimeout def _testInsideTimeout(self): self.cli = sock = socket.create_connection((HOST, PORT)) data = sock.recv(5) - self.assertEqual(data, "done!") + self.assertEqual(data, b"done!") def _testOutsideTimeout(self): self.cli = sock = socket.create_connection((HOST, PORT), timeout=1) self.failUnlessRaises(socket.timeout, lambda: sock.recv(5)) -class Urllib2FileobjectTest(unittest.TestCase): - - # urllib2.HTTPHandler has "borrowed" socket._fileobject, and requires that - # it close the socket if the close c'tor argument is true - - def testClose(self): - class MockSocket: - closed = False - def flush(self): pass - def close(self): self.closed = True - - # must not close unless we request it: the original use of _fileobject - # by module socket requires that the underlying socket not be closed until - # the _socketobject that created the _fileobject is closed - s = MockSocket() - f = socket._fileobject(s) - f.close() - self.assert_(not s.closed) - - s = MockSocket() - f = socket._fileobject(s, close=True) - f.close() - self.assert_(s.closed) - class TCPTimeoutTest(SocketTCPTest): def testTCPTimeout(self): @@ -1055,7 +1033,7 @@ class BufferIOTest(SocketConnectedTest): buf = b" "*1024 nbytes = self.cli_conn.recv_into(buf) self.assertEqual(nbytes, len(MSG)) - msg = str(buf[:len(MSG)]) + msg = buf[:len(MSG)] self.assertEqual(msg, MSG) def _testRecvInto(self): @@ -1066,7 +1044,7 @@ class BufferIOTest(SocketConnectedTest): buf = b" "*1024 nbytes, addr = self.cli_conn.recvfrom_into(buf) self.assertEqual(nbytes, len(MSG)) - msg = str(buf[:len(MSG)]) + msg = buf[:len(MSG)] self.assertEqual(msg, MSG) def _testRecvFromInto(self): @@ -1085,7 +1063,6 @@ def test_main(): UnbufferedFileObjectClassTestCase, LineBufferedFileObjectClassTestCase, SmallBufferedFileObjectClassTestCase, - Urllib2FileobjectTest, NetworkConnectionNoServer, NetworkConnectionAttributesTest, NetworkConnectionBehaviourTest, diff --git a/Lib/urllib2.py b/Lib/urllib2.py index 284c921247a..41274852ed0 100644 --- a/Lib/urllib2.py +++ b/Lib/urllib2.py @@ -1085,10 +1085,8 @@ class AbstractHTTPHandler(BaseHandler): # to read(). This weird wrapping allows the returned object to # have readline() and readlines() methods. - # XXX It might be better to extract the read buffering code - # out of socket._fileobject() and into a base class. - r.recv = r.read + # XXX socket._fileobject is gone; use some class from io.py instead fp = socket._fileobject(r, close=True) resp = addinfourl(fp, r.msg, req.get_full_url()) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 71eafe60fdf..eca29dd0709 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -19,15 +19,14 @@ Module interface: a subclass of socket.error - socket.herror: exception raised for gethostby* errors, a subclass of socket.error -- socket.fromfd(fd, family, type[, proto]) --> new socket object (created - from an existing file descriptor) - socket.gethostbyname(hostname) --> host IP address (string: 'dd.dd.dd.dd') - socket.gethostbyaddr(IP address) --> (hostname, [alias, ...], [IP addr, ...]) - socket.gethostname() --> host name (string: 'spam' or 'spam.domain.com') - socket.getprotobyname(protocolname) --> protocol number - socket.getservbyname(servicename[, protocolname]) --> port number - socket.getservbyport(portnumber[, protocolname]) --> service name -- socket.socket([family[, type [, proto]]]) --> new socket object +- socket.socket([family[, type [, proto, fileno]]]) --> new socket object + (fileno specifies a pre-existing socket file descriptor) - socket.socketpair([family[, type [, proto]]]) --> (socket, socket) - socket.ntohs(16 bit value) --> new int object - socket.ntohl(32 bit value) --> new int object @@ -102,7 +101,6 @@ getsockname() -- return local address\n\ getsockopt(level, optname[, buflen]) -- get socket options\n\ gettimeout() -- return timeout or None\n\ listen(n) -- start listening for incoming connections\n\ -makefile([mode, [bufsize]]) -- return a file object for the socket [*]\n\ recv(buflen[, flags]) -- receive data\n\ recv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer)\n\ recvfrom(buflen[, flags]) -- receive data and sender\'s address\n\ @@ -402,6 +400,10 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); #define NI_MAXSERV 32 #endif +#ifndef INVALID_SOCKET /* MS defines this */ +#define INVALID_SOCKET (-1) +#endif + /* XXX There's a problem here: *static* functions are not supposed to have a Py prefix (or use CapitalizedWords). Later... */ @@ -1551,7 +1553,7 @@ static PyObject * sock_accept(PySocketSockObject *s) { sock_addr_t addrbuf; - SOCKET_T newfd; + SOCKET_T newfd = INVALID_SOCKET; socklen_t addrlen; PyObject *sock = NULL; PyObject *addr = NULL; @@ -1562,12 +1564,6 @@ sock_accept(PySocketSockObject *s) return NULL; memset(&addrbuf, 0, addrlen); -#ifdef MS_WINDOWS - newfd = INVALID_SOCKET; -#else - newfd = -1; -#endif - if (!IS_SELECTABLE(s)) return select_error(); @@ -1582,11 +1578,7 @@ sock_accept(PySocketSockObject *s) return NULL; } -#ifdef MS_WINDOWS if (newfd == INVALID_SOCKET) -#else - if (newfd < 0) -#endif return s->errorhandler(); /* Create the new object with unspecified family, @@ -1814,16 +1806,19 @@ sock_getsockopt(PySocketSockObject *s, PyObject *args) "getsockopt buflen out of range"); return NULL; } - buf = PyString_FromStringAndSize((char *)NULL, buflen); + buf = PyBytes_FromStringAndSize((char *)NULL, buflen); if (buf == NULL) return NULL; res = getsockopt(s->sock_fd, level, optname, - (void *)PyString_AS_STRING(buf), &buflen); + (void *)PyBytes_AS_STRING(buf), &buflen); if (res < 0) { Py_DECREF(buf); return s->errorhandler(); } - _PyString_Resize(&buf, buflen); + if (PyBytes_Resize(buf, buflen) < 0) { + Py_DECREF(buf); + return NULL; + } return buf; #endif /* __BEOS__ */ } @@ -2173,69 +2168,6 @@ least 1; it specifies the number of unaccepted connection that the system\n\ will allow before refusing new connections."); -#ifndef NO_DUP -/* s.makefile(mode) method. - Create a new open file object referring to a dupped version of - the socket's file descriptor. (The dup() call is necessary so - that the open file and socket objects may be closed independent - of each other.) - The mode argument specifies 'r' or 'w' passed to fdopen(). */ - -static PyObject * -sock_makefile(PySocketSockObject *s, PyObject *args) -{ - extern int fclose(FILE *); - char *mode = "r"; - int bufsize = -1; -#ifdef MS_WIN32 - Py_intptr_t fd; -#else - int fd; -#endif - FILE *fp; - PyObject *f; -#ifdef __VMS - char *mode_r = "r"; - char *mode_w = "w"; -#endif - - if (!PyArg_ParseTuple(args, "|si:makefile", &mode, &bufsize)) - return NULL; -#ifdef __VMS - if (strcmp(mode,"rb") == 0) { - mode = mode_r; - } - else { - if (strcmp(mode,"wb") == 0) { - mode = mode_w; - } - } -#endif -#ifdef MS_WIN32 - if (((fd = _open_osfhandle(s->sock_fd, _O_BINARY)) < 0) || - ((fd = dup(fd)) < 0) || ((fp = fdopen(fd, mode)) == NULL)) -#else - if ((fd = dup(s->sock_fd)) < 0 || (fp = fdopen(fd, mode)) == NULL) -#endif - { - if (fd >= 0) - SOCKETCLOSE(fd); - return s->errorhandler(); - } - f = PyFile_FromFile(fp, "", mode, fclose); - if (f != NULL) - PyFile_SetBufSize(f, bufsize); - return f; -} - -PyDoc_STRVAR(makefile_doc, -"makefile([mode[, buffersize]]) -> file object\n\ -\n\ -Return a regular file object corresponding to the socket.\n\ -The mode and buffersize arguments are as for the built-in open() function."); - -#endif /* NO_DUP */ - /* * This is the guts of the recv() and recv_into() methods, which reads into a * char buffer. If you have any inc/dec ref to do to the objects that contain @@ -2339,12 +2271,12 @@ sock_recv(PySocketSockObject *s, PyObject *args) } /* Allocate a new string. */ - buf = PyString_FromStringAndSize((char *) 0, recvlen); + buf = PyBytes_FromStringAndSize((char *) 0, recvlen); if (buf == NULL) return NULL; /* Call the guts */ - outlen = sock_recv_guts(s, PyString_AS_STRING(buf), recvlen, flags); + outlen = sock_recv_guts(s, PyBytes_AS_STRING(buf), recvlen, flags); if (outlen < 0) { /* An error occurred, release the string and return an error. */ @@ -2354,7 +2286,7 @@ sock_recv(PySocketSockObject *s, PyObject *args) 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) + if (PyBytes_Resize(buf, outlen) < 0) /* Oopsy, not so succesful after all. */ return NULL; } @@ -2513,11 +2445,11 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args) return NULL; } - buf = PyString_FromStringAndSize((char *) 0, recvlen); + buf = PyBytes_FromStringAndSize((char *) 0, recvlen); if (buf == NULL) return NULL; - outlen = sock_recvfrom_guts(s, PyString_AS_STRING(buf), + outlen = sock_recvfrom_guts(s, PyBytes_AS_STRING(buf), recvlen, flags, &addr); if (outlen < 0) { goto finally; @@ -2526,7 +2458,7 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args) 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) + if (PyBytes_Resize(buf, outlen) < 0) /* Oopsy, not so succesful after all. */ goto finally; } @@ -2788,10 +2720,6 @@ static PyMethodDef sock_methods[] = { getsockopt_doc}, {"listen", (PyCFunction)sock_listen, METH_O, listen_doc}, -#ifndef NO_DUP - {"makefile", (PyCFunction)sock_makefile, METH_VARARGS, - makefile_doc}, -#endif {"recv", (PyCFunction)sock_recv, METH_VARARGS, recv_doc}, {"recv_into", (PyCFunction)sock_recv_into, METH_VARARGS | METH_KEYWORDS, @@ -2861,7 +2789,7 @@ sock_repr(PySocketSockObject *s) #endif PyOS_snprintf( buf, sizeof(buf), - "", + "", (long)s->sock_fd, s->sock_family, s->sock_type, s->sock_proto); @@ -2893,27 +2821,35 @@ static int sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) { PySocketSockObject *s = (PySocketSockObject *)self; - SOCKET_T fd; + PyObject *fdobj = NULL; + SOCKET_T fd = INVALID_SOCKET; int family = AF_INET, type = SOCK_STREAM, proto = 0; - static char *keywords[] = {"family", "type", "proto", 0}; + static char *keywords[] = {"family", "type", "proto", "fileno", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwds, - "|iii:socket", keywords, - &family, &type, &proto)) + "|iiiO:socket", keywords, + &family, &type, &proto, &fdobj)) return -1; - Py_BEGIN_ALLOW_THREADS - fd = socket(family, type, proto); - Py_END_ALLOW_THREADS + if (fdobj != NULL) { + fd = PyLong_AsLongLong(fdobj); + if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) + return -1; + if (fd == INVALID_SOCKET) { + PyErr_SetString(PyExc_ValueError, + "can't use invalid socket value"); + return -1; + } + } + else { + Py_BEGIN_ALLOW_THREADS + fd = socket(family, type, proto); + Py_END_ALLOW_THREADS -#ifdef MS_WINDOWS - if (fd == INVALID_SOCKET) -#else - if (fd < 0) -#endif - { - set_error(); - return -1; + if (fd == INVALID_SOCKET) { + set_error(); + return -1; + } } init_sockobject(s, fd, family, type, proto); @@ -3462,39 +3398,6 @@ AF_UNIX if defined on the platform; otherwise, the default is AF_INET."); #endif /* HAVE_SOCKETPAIR */ -#ifndef NO_DUP -/* Create a socket object from a numeric file description. - Useful e.g. if stdin is a socket. - Additional arguments as for socket(). */ - -/*ARGSUSED*/ -static PyObject * -socket_fromfd(PyObject *self, PyObject *args) -{ - PySocketSockObject *s; - SOCKET_T fd; - int family, type, proto = 0; - if (!PyArg_ParseTuple(args, "iii|i:fromfd", - &fd, &family, &type, &proto)) - return NULL; - /* Dup the fd so it and the socket can be closed independently */ - fd = dup(fd); - if (fd < 0) - return set_error(); - s = new_sockobject(fd, family, type, proto); - return (PyObject *) s; -} - -PyDoc_STRVAR(fromfd_doc, -"fromfd(fd, family, type[, proto]) -> socket object\n\ -\n\ -Create a socket object from a duplicate of the given\n\ -file descriptor.\n\ -The remaining arguments are the same as for socket()."); - -#endif /* NO_DUP */ - - static PyObject * socket_ntohs(PyObject *self, PyObject *args) { @@ -3613,7 +3516,7 @@ Convert a 32-bit integer from host to network byte order."); /* socket.inet_aton() and socket.inet_ntoa() functions. */ PyDoc_STRVAR(inet_aton_doc, -"inet_aton(string) -> packed 32-bit IP representation\n\ +"inet_aton(string) -> bytes giving packed 32-bit IP representation\n\ \n\ Convert an IP address in string format (123.45.67.89) to the 32-bit packed\n\ binary format used in low-level network functions."); @@ -3644,7 +3547,7 @@ socket_inet_aton(PyObject *self, PyObject *args) if (inet_aton != NULL) { #endif if (inet_aton(ip_addr, &buf)) - return PyString_FromStringAndSize((char *)(&buf), + return PyBytes_FromStringAndSize((char *)(&buf), sizeof(buf)); PyErr_SetString(socket_error, @@ -3673,8 +3576,8 @@ socket_inet_aton(PyObject *self, PyObject *args) return NULL; } } - return PyString_FromStringAndSize((char *) &packed_addr, - sizeof(packed_addr)); + return PyBytes_FromStringAndSize((char *) &packed_addr, + sizeof(packed_addr)); #ifdef USE_INET_ATON_WEAKLINK } @@ -4077,10 +3980,6 @@ static PyMethodDef socket_methods[] = { METH_VARARGS, getservbyport_doc}, {"getprotobyname", socket_getprotobyname, METH_VARARGS, getprotobyname_doc}, -#ifndef NO_DUP - {"fromfd", socket_fromfd, - METH_VARARGS, fromfd_doc}, -#endif #ifdef HAVE_SOCKETPAIR {"socketpair", socket_socketpair, METH_VARARGS, socketpair_doc}, @@ -4229,14 +4128,11 @@ PySocketModule_APIObject PySocketModuleAPI = /* Initialize the _socket module. This module is actually called "_socket", and there's a wrapper - "socket.py" which implements some additional functionality. On some - platforms (e.g. Windows and OS/2), socket.py also implements a - wrapper for the socket type that provides missing functionality such - as makefile(), dup() and fromfd(). The import of "_socket" may fail - with an ImportError exception if os-specific initialization fails. - On Windows, this does WINSOCK initialization. When WINSOCK is - initialized succesfully, a call to WSACleanup() is scheduled to be - made at exit time. + "socket.py" which implements some additional functionality. + The import of "_socket" may fail with an ImportError exception if + os-specific initialization fails. On Windows, this does WINSOCK + initialization. When WINSOCK is initialized succesfully, a call to + WSACleanup() is scheduled to be made at exit time. */ PyDoc_STRVAR(socket_doc,