Index: Include/longobject.h =================================================================== --- Include/longobject.h (revision 58987) +++ Include/longobject.h (working copy) @@ -26,6 +26,15 @@ PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); +/* Used by socketmodule.c */ +#if SIZEOF_SOCKET_T <= SIZEOF_LONG +#define PyLong_FromSocket_t(fd) PyLong_FromLong((SOCKET_T)(fd)) +#define PyLong_AsSocket_t(fd) (SOCKET_T)PyLong_AsLong(fd) +#else +#define PyLong_FromSocket_t(fd) PyLong_FromLongLong(((SOCKET_T)(fd)); +#define PyLong_AsSocket_t(fd) (SOCKET_T)PyLong_AsLongLong(fd) +#endif + /* For use by intobject.c only */ PyAPI_DATA(int) _PyLong_DigitValue[256]; Index: Lib/socket.py =================================================================== --- Lib/socket.py (revision 58987) +++ Lib/socket.py (working copy) @@ -79,28 +79,19 @@ __all__.append("errorTab") -# True if os.dup() can duplicate socket descriptors. -# (On Windows at least, os.dup only works on files) -_can_dup_socket = hasattr(_socket.socket, "dup") +def socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): + """Factory function for socket objects.""" + return Socket(family, type, proto, fileno) -if _can_dup_socket: - 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): +class Socket(_socket.socket): """A subclass of _socket.socket adding the makefile() method.""" __slots__ = ["__weakref__", "_io_refs", "_closed"] - if not _can_dup_socket: - __slots__.append("_base") def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None): - if fileno is None: - _socket.socket.__init__(self, family, type, proto) - else: - _socket.socket.__init__(self, family, type, proto, fileno) + _socket.socket.__init__(self, family, type, proto, fileno) self._io_refs = 0 self._closed = False @@ -114,23 +105,27 @@ s[7:]) return s + def dup(self): + """dup() -> socket object + + Return a new socket object connected to the same system resource. + """ + fd = dup(self.fileno()) + return self.__class__(self.family, self.type, self.proto, fileno=fd) + def accept(self): - """Wrap accept() to give the connection the right type.""" - conn, addr = _socket.socket.accept(self) - fd = conn.fileno() - nfd = fd - if _can_dup_socket: - nfd = os.dup(fd) - wrapper = socket(self.family, self.type, self.proto, fileno=nfd) - if fd == nfd: - wrapper._base = conn # Keep the base alive - else: - conn.close() - return wrapper, addr + """accept() -> (socket object, address info) + Wait for an incoming connection. Return a new socket + representing the connection, and the address of the client. + For IP sockets, the address info is a pair (hostaddr, port). + """ + fd, addr = self._accept() + return Socket(self.family, self.type, self.proto, fileno=fd), addr + def makefile(self, mode="r", buffering=None, *, encoding=None, newline=None): - """Return an I/O stream connected to the socket. + """makefile(...) -> an I/O stream connected to the socket The arguments are as for io.open() after the filename, except the only mode characters supported are 'r', 'w' and 'b'. @@ -148,8 +143,8 @@ rawmode += "r" if writing: rawmode += "w" - raw = SocketIO(self, rawmode) - self._io_refs += 1 + sock = self.dup() + raw = SocketIO(sock, rawmode) if buffering is None: buffering = -1 if buffering < 0: @@ -157,7 +152,7 @@ if buffering == 0: if not binary: raise ValueError("unbuffered streams must be binary") - raw.name = self.fileno() + raw.name = sock.fileno() raw.mode = mode return raw if reading and writing: @@ -168,39 +163,25 @@ assert writing buffer = io.BufferedWriter(raw, buffering) if binary: - buffer.name = self.fileno() + buffer.name = sock.fileno() buffer.mode = mode return buffer text = io.TextIOWrapper(buffer, encoding, newline) - text.name = self.fileno() + text.name = sock.fileno() text.mode = mode return text - def _decref_socketios(self): - if self._io_refs > 0: - self._io_refs -= 1 - if self._closed: - self.close() - def close(self): - self._closed = True - if self._io_refs < 1: - self._real_close() +def fromfd(fd, family, type, proto=0): + """ fromfd(fd, family, type[, proto]) -> socket object - # _real_close calls close on the _socket.socket base class. + Create a socket object from a duplicate of the given file + descriptor. The remaining arguments are the same as for socket(). + """ + nfd = dup(fd) + return Socket(family, type, proto, nfd) - if not _can_dup_socket: - def _real_close(self): - _socket.socket.close(self) - base = getattr(self, "_base", None) - if base is not None: - self._base = None - base.close() - else: - def _real_close(self): - _socket.socket.close(self) - class SocketIO(io.RawIOBase): """Raw I/O implementation for stream sockets. @@ -243,11 +224,9 @@ if self.closed: return io.RawIOBase.close(self) + self._sock = None - def __del__(self): - self._sock._decref_socketios() - def getfqdn(name=''): """Get fully qualified domain name from name. @@ -289,7 +268,7 @@ af, socktype, proto, canonname, sa = res sock = None try: - sock = socket(af, socktype, proto) + sock = Socket(af, socktype, proto) if timeout is not None: sock.settimeout(timeout) sock.connect(sa) Index: Lib/test/test_socket.py =================================================================== --- Lib/test/test_socket.py (revision 58987) +++ Lib/test/test_socket.py (working copy) @@ -224,7 +224,7 @@ def test_repr(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.assert_(repr(s).startswith("sock_timeout is in seconds, timeout in ms */ - timeout = (int)(s->sock_timeout * 1000 + 0.5); + timeout = (int)(s->sock_timeout * 1000 + 0.5); n = poll(&pollfd, 1, timeout); } #else @@ -721,7 +737,7 @@ n = select(s->sock_fd+1, &fds, NULL, NULL, &tv); } #endif - + if (n < 0) return -1; if (n == 0) @@ -1496,7 +1512,7 @@ } -/* s.accept() method */ +/* s._accept() -> (fd, address) */ static PyObject * sock_accept(PySocketSockObject *s) @@ -1530,17 +1546,12 @@ if (newfd == INVALID_SOCKET) return s->errorhandler(); - /* Create the new object with unspecified family, - to avoid calls to bind() etc. on it. */ - sock = (PyObject *) new_sockobject(newfd, - s->sock_family, - s->sock_type, - s->sock_proto); - + sock = PyLong_FromSocket_t(newfd); if (sock == NULL) { SOCKETCLOSE(newfd); goto finally; } + addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, s->sock_proto); if (addr == NULL) @@ -1555,11 +1566,11 @@ } PyDoc_STRVAR(accept_doc, -"accept() -> (socket object, address info)\n\ +"_accept() -> (integer, address info)\n\ \n\ -Wait for an incoming connection. Return a new socket representing the\n\ -connection, and the address of the client. For IP sockets, the address\n\ -info is a pair (hostaddr, port)."); +Wait for an incoming connection. Return a new socket file descriptor\n\ +representing the connection, and the address of the client.\n\ +For IP sockets, the address info is a pair (hostaddr, port)."); /* s.setblocking(flag) method. Argument: False -- non-blocking mode; same as settimeout(0) @@ -1955,11 +1966,7 @@ static PyObject * sock_fileno(PySocketSockObject *s) { -#if SIZEOF_SOCKET_T <= SIZEOF_LONG - return PyInt_FromLong((long) s->sock_fd); -#else - return PyLong_FromLongLong((PY_LONG_LONG)s->sock_fd); -#endif + return PyLong_FromSocket_t(s->sock_fd); } PyDoc_STRVAR(fileno_doc, @@ -1968,35 +1975,6 @@ Return the integer file descriptor of the socket."); -#ifndef NO_DUP -/* s.dup() method */ - -static PyObject * -sock_dup(PySocketSockObject *s) -{ - SOCKET_T newfd; - PyObject *sock; - - newfd = dup(s->sock_fd); - if (newfd < 0) - return s->errorhandler(); - sock = (PyObject *) new_sockobject(newfd, - s->sock_family, - s->sock_type, - s->sock_proto); - if (sock == NULL) - SOCKETCLOSE(newfd); - return sock; -} - -PyDoc_STRVAR(dup_doc, -"dup() -> socket object\n\ -\n\ -Return a new socket object connected to the same system resource."); - -#endif - - /* s.getsockname() method */ static PyObject * @@ -2615,7 +2593,7 @@ /* List of methods for socket objects */ static PyMethodDef sock_methods[] = { - {"accept", (PyCFunction)sock_accept, METH_NOARGS, + {"_accept", (PyCFunction)sock_accept, METH_NOARGS, accept_doc}, {"bind", (PyCFunction)sock_bind, METH_O, bind_doc}, @@ -2625,10 +2603,6 @@ connect_doc}, {"connect_ex", (PyCFunction)sock_connect_ex, METH_O, connect_ex_doc}, -#ifndef NO_DUP - {"dup", (PyCFunction)sock_dup, METH_NOARGS, - dup_doc}, -#endif {"fileno", (PyCFunction)sock_fileno, METH_NOARGS, fileno_doc}, #ifdef HAVE_GETPEERNAME @@ -2745,8 +2719,8 @@ &family, &type, &proto, &fdobj)) return -1; - if (fdobj != NULL) { - fd = PyLong_AsLongLong(fdobj); + if (fdobj != NULL && fdobj != Py_None) { + fd = PyLong_AsSocket_t(fdobj); if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) return -1; if (fd == INVALID_SOCKET) { @@ -3245,6 +3219,38 @@ Return the protocol number for the named protocol. (Rarely used.)"); +#ifndef NO_DUP +/* dup() function for socket fds */ + +static PyObject * +socket_dup(PyObject *self, PyObject *fdobj) +{ + SOCKET_T fd, newfd; + PyObject *newfdobj; + + + fd = PyLong_AsSocket_t(fdobj); + if (fd == (SOCKET_T)(-1) && PyErr_Occurred()) + return NULL; + + newfd = dup_socket(fd); + if (newfd == INVALID_SOCKET) + return set_error(); + + newfdobj = PyLong_FromSocket_t(newfd); + if (newfdobj == NULL) + SOCKETCLOSE(newfd); + return newfdobj; +} + +PyDoc_STRVAR(dup_doc, +"dup(integer) -> integer\n\ +\n\ +Duplicate an integer socket file descriptor. This is like os.dup(), but for\n\ +sockets; on some platforms os.dup() won't work for socket file descriptors."); +#endif + + #ifdef HAVE_SOCKETPAIR /* Create a pair of sockets using the socketpair() function. Arguments as for socket() except the default family is AF_UNIX if @@ -3884,6 +3890,10 @@ METH_VARARGS, getservbyport_doc}, {"getprotobyname", socket_getprotobyname, METH_VARARGS, getprotobyname_doc}, +#ifndef NO_DUP + {"dup", socket_dup, + METH_O, dup_doc}, +#endif #ifdef HAVE_SOCKETPAIR {"socketpair", socket_socketpair, METH_VARARGS, socketpair_doc}, @@ -4178,7 +4188,7 @@ PyModule_AddIntConstant(m, "NETLINK_IP6_FW", NETLINK_IP6_FW); #ifdef NETLINK_DNRTMSG PyModule_AddIntConstant(m, "NETLINK_DNRTMSG", NETLINK_DNRTMSG); -#endif +#endif #ifdef NETLINK_TAPBASE PyModule_AddIntConstant(m, "NETLINK_TAPBASE", NETLINK_TAPBASE); #endif