diff -r 0f827775f7b7 Lib/telnetlib.py --- a/Lib/telnetlib.py Wed Feb 13 21:17:13 2013 -0500 +++ b/Lib/telnetlib.py Fri Feb 15 15:00:15 2013 +1100 @@ -312,7 +312,7 @@ poller.register(self, poll_in_or_priority_flags) while i < 0 and not self.eof: try: - ready = poller.poll(call_timeout) + ready = poller.poll(1000*call_timeout if call_timeout else None) except OSError as e: if e.errno == errno.EINTR: if timeout is not None: @@ -326,6 +326,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: @@ -345,34 +347,34 @@ The timeout is implemented using select.select(). """ n = len(match) + s_args = [[self.sock], [], [], timeout] + if timeout is not None: + from time import time + 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,) - from time import time - 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): @@ -682,7 +684,7 @@ poller.register(self, poll_in_or_priority_flags) while not m and not self.eof: try: - ready = poller.poll(call_timeout) + ready = poller.poll(1000*call_timeout if call_timeout else None) except OSError as e: if e.errno == errno.EINTR: if timeout is not None: @@ -744,9 +746,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: + continue break self.fill_rawq() text = self.read_very_lazy() diff -r 0f827775f7b7 Lib/test/test_telnetlib.py --- a/Lib/test/test_telnetlib.py Wed Feb 13 21:17:13 2013 -0500 +++ b/Lib/test/test_telnetlib.py Fri Feb 15 15:00:15 2013 +1100 @@ -84,6 +84,9 @@ 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) @@ -106,14 +109,31 @@ 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. @@ -132,9 +152,20 @@ 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) @@ -297,6 +328,64 @@ self.assertTrue(want.startswith(data)) self.assertEqual(data, want) + def test_read_until_timeout_with_poll(self, use_poll=True): + expected_duration = 1 + tolerance = 0.01 + 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.assertTrue(abs(duration - expected_duration) / expected_duration < tolerance) + + 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.assertTrue(abs(duration - expected_duration) / expected_duration < tolerance) + + 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 + tolerance = 0.01 + 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.assertTrue(abs(duration - expected_duration) / expected_duration < tolerance) + + 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.assertTrue(abs(duration - expected_duration) / expected_duration < tolerance) + + 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''