# HG changeset patch # Parent 9d51225480966a61b22fc45e62c2d50373d96ca8 diff -r 9d5122548096 Doc/library/select.rst --- a/Doc/library/select.rst Sun Dec 16 21:11:35 2012 +0100 +++ b/Doc/library/select.rst Sun Dec 16 22:20:11 2012 +0000 @@ -63,6 +63,10 @@ for I/O events; see section :ref:`poll-objects` below for the methods supported by polling objects. + .. versionchanged:: 3.4 + Support added for Windows Vista and later versions of Windows. + As with :func:`select`, only sockets and socket handles are supported. + .. function:: kqueue() diff -r 9d5122548096 Lib/test/test_poll.py --- a/Lib/test/test_poll.py Sun Dec 16 21:11:35 2012 +0100 +++ b/Lib/test/test_poll.py Sun Dec 16 22:20:11 2012 +0000 @@ -1,13 +1,40 @@ # Test case for the os.poll() function -import os, select, random, unittest, subprocess +import os, select, random, unittest, subprocess, time, socket, sys from test.support import TESTFN, run_unittest +from test.script_helper import spawn_python try: select.poll except AttributeError: raise unittest.SkipTest("select.poll not defined") +WIN32 = (sys.platform == 'win32') + +if WIN32: + def pipe(): + with socket.socket() as l: + l.bind(('127.0.0.1', 0)) + l.listen(1) + c = socket.socket() + c.connect(l.getsockname()) + a, address = l.accept() + return c.detach(), a.detach() + + def read(fd, nbytes): + with socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) as s: + return s.recv(nbytes) + + def write(fd, buf): + with socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) as s: + return s.sendall(buf) + + def close(fd): + socket.socket(fileno=fd).close() + +else: + from os import pipe, read, write, close + def find_ready_matching(ready, flag): match = [] @@ -33,7 +60,7 @@ w2r = {} for i in range(NUM_PIPES): - rd, wr = os.pipe() + rd, wr = pipe() p.register(rd) p.modify(rd, select.POLLIN) p.register(wr, select.POLLOUT) @@ -50,17 +77,17 @@ if not ready_writers: raise RuntimeError("no pipes ready for writing") wr = random.choice(ready_writers) - os.write(wr, MSG) + write(wr, MSG) ready = p.poll() ready_readers = find_ready_matching(ready, select.POLLIN) if not ready_readers: raise RuntimeError("no pipes ready for reading") rd = random.choice(ready_readers) - buf = os.read(rd, MSG_LEN) + buf = read(rd, MSG_LEN) self.assertEqual(len(buf), MSG_LEN) bufs.append(buf) - os.close(r2w[rd]) ; os.close( rd ) + close(r2w[rd]) ; close( rd ) p.unregister( r2w[rd] ) p.unregister( rd ) writers.remove(r2w[rd]) @@ -69,24 +96,29 @@ def test_poll_unit_tests(self): # returns NVAL for invalid file descriptor - FD, w = os.pipe() - os.close(FD) - os.close(w) + FD1, FD2 = pipe() + close(FD1) + close(FD2) p = select.poll() - p.register(FD) + p.register(FD1) r = p.poll() - self.assertEqual(r[0], (FD, select.POLLNVAL)) + self.assertEqual(r, [(FD1, select.POLLNVAL)]) + p.register(FD2) + r = p.poll() + self.assertEqual(set(r), + {(FD1, select.POLLNVAL), (FD2, select.POLLNVAL)}) - f = open(TESTFN, 'w') - fd = f.fileno() - p = select.poll() - p.register(f) - r = p.poll() - self.assertEqual(r[0][0], fd) - f.close() - r = p.poll() - self.assertEqual(r[0], (fd, select.POLLNVAL)) - os.unlink(TESTFN) + if not WIN32: + f = open(TESTFN, 'w') + fd = f.fileno() + p = select.poll() + p.register(f) + r = p.poll() + self.assertEqual(r[0][0], fd) + f.close() + r = p.poll() + self.assertEqual(r[0], (fd, select.POLLNVAL)) + os.unlink(TESTFN) # type error for invalid arguments p = select.poll() @@ -112,11 +144,15 @@ # Another test case for poll(). This is copied from the test case for # select(), modified to use poll() instead. + @unittest.skipIf(WIN32, 'skipped on Windows') def test_poll2(self): cmd = 'for i in 0 1 2 3 4 5 6 7 8 9; do echo testing...; sleep 1; done' proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=0) - p = proc.stdout + with proc, proc.stdout: + self.common_poll2(proc.stdout) + + def common_poll2(self, p): pollster = select.poll() pollster.register( p, select.POLLIN ) for tout in (0, 1000, 2000, 4000, 8000, 16000) + (-1,)*10: @@ -140,6 +176,29 @@ self.fail('Unexpected return value from select.poll: %s' % fdlist) p.close() + def test_poll2_socket(self): + cmd = 'from test.test_poll import PollTests; PollTests.child_poll2(%s)' + with socket.socket() as listener: + listener.bind(('127.0.0.1', 0)) + listener.listen(1) + cmd %= listener.getsockname()[1] + subprocess.Popen([sys.executable, '-c', cmd]) + listener.settimeout(20) + with listener.accept()[0] as a: + listener.settimeout(None) + with a.makefile(mode='rb', buffering=0) as p: + self.common_poll2(p) + + @staticmethod + def child_poll2(port): + with socket.socket() as s: + s.connect(('127.0.0.1', port)) + with s.makefile(mode='wb', buffering=0) as f: + for i in range(10): + f.write(b'testing...\n') + time.sleep(0.2) + + def test_poll3(self): # test int overflow pollster = select.poll() diff -r 9d5122548096 Modules/selectmodule.c --- a/Modules/selectmodule.c Sun Dec 16 21:11:35 2012 +0100 +++ b/Modules/selectmodule.c Sun Dec 16 22:20:11 2012 +0000 @@ -48,8 +48,14 @@ #endif #ifdef MS_WINDOWS +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0600 +# undef NTDDI_VERSION +# define NTDDI_VERSION NTDDI_LONGHORN # define WIN32_LEAN_AND_MEAN -# include +# include +# include +# define HAVE_POLL #else # define SOCKET int # if defined(__VMS) @@ -321,6 +327,35 @@ * poll() support */ +#ifdef MS_WINDOWS +static LPFN_WSAPOLL Py_WSAPoll = NULL; + +static int +poll(struct pollfd *fds, int nfds, int timeout) +{ + /* The return value for WSAPoll() seems not to count items for + which revents is POLLNVAL; if all revents fields are POLLNVAL + then -1 is returned and WSAGetLastError() == WSAENOTSOCK. */ + int res, j; + res = Py_WSAPoll(fds, (ULONG)nfds, timeout); + if (res < 0 && WSAGetLastError() == WSAENOTSOCK) { + /* Double check that revents == POLLNVAL for each array item */ + for (j = 0; j < nfds; j++) + if (fds[j].revents != POLLNVAL) + return -1; + return nfds; + } + if (res >= 0) { + /* Count *all* nonzero revents fields */ + res = 0; + for (j = 0; j < nfds; j++) + if (fds[j].revents != 0) + res += 1; + } + return res; +} +#endif + typedef struct { PyObject_HEAD PyObject *dict; @@ -371,7 +406,12 @@ poll_register(pollObject *self, PyObject *args) { PyObject *o, *key, *value; +#ifndef MS_WINDOWS int fd, events = POLLIN | POLLPRI | POLLOUT; +#else + /* Requesting the POLLPRI event fails on Windows */ + int fd, events = POLLIN | POLLOUT; +#endif int err; if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) { @@ -530,7 +570,11 @@ Py_END_ALLOW_THREADS if (poll_result < 0) { +#ifndef MS_WINDOWS PyErr_SetFromErrno(PyExc_OSError); +#else + PyErr_SetFromWindowsErr(WSAGetLastError()); +#endif return NULL; } @@ -551,7 +595,11 @@ value = PyTuple_New(2); if (value == NULL) goto error; +#ifndef MS_WINDOWS num = PyLong_FromLong(self->ufds[i].fd); +#else + num = PyLong_FromSize_t((size_t)self->ufds[i].fd); +#endif if (num == NULL) { Py_DECREF(value); goto error; @@ -2146,6 +2194,17 @@ PyInit_select(void) { PyObject *m; + +#ifdef MS_WINDOWS + HINSTANCE hWs2_32 = GetModuleHandle("WS2_32"); + if (!hWs2_32) { + PyErr_SetString(PyExc_RuntimeError, "could not get handle for Ws2_32"); + return NULL; + } + /* On WinXP WSAPoll is unavailable so Py_WSAPoll will be NULL */ + *(FARPROC *)&Py_WSAPoll = GetProcAddress(hWs2_32, "WSAPoll"); +#endif + m = PyModule_Create(&selectmodule); if (m == NULL) return NULL; @@ -2168,6 +2227,8 @@ PyErr_Clear(); } } else { +#elif defined(MS_WINDOWS) + if (Py_WSAPoll != NULL) { #else { #endif