diff -r a951ab03bda0 Lib/telnetlib.py --- a/Lib/telnetlib.py Mon Dec 09 00:25:57 2013 +0100 +++ b/Lib/telnetlib.py Mon Dec 09 21:07:12 2013 +0200 @@ -315,8 +315,9 @@ poller.register(self, poll_in_or_priority_flags) while i < 0 and not self.eof: try: - ready = poller.poll(call_timeout) - except select.error as e: + ready = poller.poll(None if timeout is None + else 1000 * call_timeout) + except OSError as e: if e.errno == errno.EINTR: if timeout is not None: elapsed = _time() - time_start @@ -329,6 +330,8 @@ self.fill_rawq() self.process_rawq() i = self.cookedq.find(match, i) + if i >= 0: + break if timeout is not None: elapsed = _time() - time_start if elapsed >= timeout: @@ -348,33 +351,33 @@ The timeout is implemented using select.select(). """ n = len(match) + s_args = [[self.sock], [], [], timeout] + if timeout is not None: + time_start = _time() self.process_rawq() i = self.cookedq.find(match) + + if i < 0: + s_reply = ([self.sock], [], []) + while i < 0 and not self.eof: + s_reply = select.select(*s_args) + if self.sock in s_reply[0]: + i = max(0, len(self.cookedq) - n) + self.fill_rawq() + self.process_rawq() + i = self.cookedq.find(match, i) + if i >= 0: + break + if timeout is not None: + elapsed = _time() - time_start + if elapsed >= timeout: + break + s_args[3] = timeout - elapsed if i >= 0: i = i+n buf = self.cookedq[:i] self.cookedq = self.cookedq[i:] return buf - s_reply = ([self], [], []) - s_args = s_reply - if timeout is not None: - s_args = s_args + (timeout,) - time_start = _time() - while not self.eof and select.select(*s_args) == s_reply: - i = max(0, len(self.cookedq)-n) - self.fill_rawq() - self.process_rawq() - i = self.cookedq.find(match, i) - if i >= 0: - i = i+n - buf = self.cookedq[:i] - self.cookedq = self.cookedq[i:] - return buf - if timeout is not None: - elapsed = _time() - time_start - if elapsed >= timeout: - break - s_args = s_reply + (timeout-elapsed,) return self.read_very_lazy() def read_all(self): @@ -683,8 +686,9 @@ poller.register(self, poll_in_or_priority_flags) while not m and not self.eof: try: - ready = poller.poll(call_timeout) - except select.error as e: + ready = poller.poll(None if timeout is None + else 1000 * call_timeout) + except OSError as e: if e.errno == errno.EINTR: if timeout is not None: elapsed = _time() - time_start @@ -744,10 +748,10 @@ elapsed = _time() - time_start if elapsed >= timeout: break - s_args = ([self.fileno()], [], [], timeout-elapsed) + s_args = ([self.sock], [], [], timeout-elapsed) r, w, x = select.select(*s_args) if not r: - break + continue self.fill_rawq() text = self.read_very_lazy() if not text and self.eof: diff -r a951ab03bda0 Lib/test/test_telnetlib.py --- a/Lib/test/test_telnetlib.py Mon Dec 09 00:25:57 2013 +0100 +++ b/Lib/test/test_telnetlib.py Mon Dec 09 21:07:12 2013 +0200 @@ -92,6 +92,8 @@ def sendall(self, data): self.writes.append(data) def recv(self, size): + if self.block: + raise socket.timeout() out = b'' while self.reads and len(out) < size: out += self.reads.pop(0) @@ -114,14 +116,29 @@ def mock_select(*s_args): block = False - for l in s_args: + for l in s_args[:3]: for fob in l: if isinstance(fob, TelnetAlike): block = fob.sock.block + elif isinstance(fob, SocketStub): + block = fob.block if block: + if len(s_args) > 3: + time.sleep(s_args[3]) #timeout is given in seconds return [[], [], []] else: - return s_args + #only return _file_objs which have data available + result = [[], s_args[1], s_args[2]] + for fob in s_args[0]: + if isinstance(fob, TelnetAlike): + if fob.sock.reads: + result[0].append(fob) + elif isinstance(fob, SocketStub): + if fob.reads: + result[0].append(fob) + else: + result[0].append(fob) + return result class MockPoller(object): test_case = None # Set during TestCase setUp. @@ -140,9 +157,19 @@ if isinstance(fob, TelnetAlike): block = fob.sock.block if block: + if timeout: + time.sleep(timeout / 1000) #timeout is given in milliseconds return [] else: - return zip(self._file_objs, [select.POLLIN]*len(self._file_objs)) + #only return _file_objs which have data available + result = [] + for fob in self._file_objs: + if isinstance(fob, TelnetAlike): + if fob.sock.reads: + result.append((fob, select.POLLIN)) + else: + result.append((fob, select.POLLIN)) + return result def unregister(self, fd): self._file_objs.remove(fd) @@ -305,6 +332,60 @@ self.assertTrue(want.startswith(data)) self.assertEqual(data, want) + def test_read_until_timeout_with_poll(self, use_poll=True): + expected_duration = 1 + want = [b'foo'] + if use_poll: + select.select = lambda *_: self.fail('unexpected select() call.') + else: + select.poll = lambda *_: self.fail('unexpected poll() call.') + + telnet = test_telnet(want, use_poll=use_poll) + + telnet.sock.block = True #no data available + started = time.time() + data = telnet.read_until(b'bar', expected_duration) + duration = time.time() - started + self.assertEqual(data, b'') + self.assertAlmostEqual(duration, expected_duration, places=2) + + telnet.sock.block = False #wrong data available + started = time.time() + data = telnet.read_until(b'bar', expected_duration) + duration = time.time() - started + self.assertEqual(data, b'foo') + self.assertAlmostEqual(duration, expected_duration, places=2) + + def test_read_until_timeout_with_select(self): + self.test_read_until_timeout_with_poll(False) + + def test_expect_timeout_with_poll(self, use_poll=True): + expected_duration = 1 + want = [b'foo'] + if use_poll: + select.select = lambda *_: self.fail('unexpected select() call.') + else: + select.poll = lambda *_: self.fail('unexpected poll() call.') + + telnet = test_telnet(want, use_poll=use_poll) + + telnet.sock.block = True #no data available + started = time.time() + data = telnet.expect([b'bar'], expected_duration) + duration = time.time() - started + self.assertEqual(data, (-1, None, b'')) + self.assertAlmostEqual(duration, expected_duration, places=2) + + telnet.sock.block = False #wrong available + started = time.time() + data = telnet.expect([b'bar'], expected_duration) + duration = time.time() - started + self.assertEqual(data, (-1, None, b'foo')) + self.assertAlmostEqual(duration, expected_duration, places=2) + + def test_expect_timeout_with_select(self): + self.test_expect_timeout_with_poll(False) + class nego_collector(object): def __init__(self, sb_getter=None): self.seen = b''