diff options
author | Gregory P. Smith <greg@krypto.org> | 2012-07-16 06:42:26 (GMT) |
---|---|---|
committer | Gregory P. Smith <greg@krypto.org> | 2012-07-16 06:42:26 (GMT) |
commit | dad5711677f965a62713bfc9a34599c8f44572e8 (patch) | |
tree | 336b01e8761c7ff2baecefa3cc67f49a6e3a8245 /Lib/telnetlib.py | |
parent | 4774946c3b7564b4a1c93da4ba4dba443a36a708 (diff) | |
download | cpython-dad5711677f965a62713bfc9a34599c8f44572e8.zip cpython-dad5711677f965a62713bfc9a34599c8f44572e8.tar.gz cpython-dad5711677f965a62713bfc9a34599c8f44572e8.tar.bz2 |
Fixes Issue #14635: telnetlib will use poll() rather than select() when possible
to avoid failing due to the select() file descriptor limit.
Diffstat (limited to 'Lib/telnetlib.py')
-rw-r--r-- | Lib/telnetlib.py | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index 82b5e8f..a59693e 100644 --- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -34,6 +34,7 @@ To do: # Imported modules +import errno import sys import socket import select @@ -205,6 +206,7 @@ class Telnet: self.sb = 0 # flag for SB and SE sequence. self.sbdataq = b'' self.option_callback = None + self._has_poll = hasattr(select, 'poll') if host is not None: self.open(host, port, timeout) @@ -287,6 +289,61 @@ class Telnet: is closed and no cooked data is available. """ + if self._has_poll: + return self._read_until_with_poll(match, timeout) + else: + return self._read_until_with_select(match, timeout) + + def _read_until_with_poll(self, match, timeout): + """Read until a given string is encountered or until timeout. + + This method uses select.poll() to implement the timeout. + """ + n = len(match) + call_timeout = timeout + if timeout is not None: + from time import time + time_start = time() + self.process_rawq() + i = self.cookedq.find(match) + if i < 0: + poller = select.poll() + poll_in_or_priority_flags = select.POLLIN | select.POLLPRI + 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: + if e.errno == errno.EINTR: + if timeout is not None: + elapsed = time() - time_start + call_timeout = timeout-elapsed + continue + raise + for fd, mode in ready: + if mode & poll_in_or_priority_flags: + i = max(0, len(self.cookedq)-n) + self.fill_rawq() + self.process_rawq() + i = self.cookedq.find(match, i) + if timeout is not None: + elapsed = time() - time_start + if elapsed >= timeout: + break + call_timeout = timeout-elapsed + poller.unregister(self) + if i >= 0: + i = i + n + buf = self.cookedq[:i] + self.cookedq = self.cookedq[i:] + return buf + return self.read_very_lazy() + + def _read_until_with_select(self, match, timeout=None): + """Read until a given string is encountered or until timeout. + + The timeout is implemented using select.select(). + """ n = len(match) self.process_rawq() i = self.cookedq.find(match) @@ -589,6 +646,79 @@ class Telnet: results are undeterministic, and may depend on the I/O timing. """ + if self._has_poll: + return self._expect_with_poll(list, timeout) + else: + return self._expect_with_select(list, timeout) + + def _expect_with_poll(self, expect_list, timeout=None): + """Read until one from a list of a regular expressions matches. + + This method uses select.poll() to implement the timeout. + """ + re = None + expect_list = expect_list[:] + indices = range(len(expect_list)) + for i in indices: + if not hasattr(expect_list[i], "search"): + if not re: import re + expect_list[i] = re.compile(expect_list[i]) + call_timeout = timeout + if timeout is not None: + from time import time + time_start = time() + self.process_rawq() + m = None + for i in indices: + m = expect_list[i].search(self.cookedq) + if m: + e = m.end() + text = self.cookedq[:e] + self.cookedq = self.cookedq[e:] + break + if not m: + poller = select.poll() + poll_in_or_priority_flags = select.POLLIN | select.POLLPRI + 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: + if e.errno == errno.EINTR: + if timeout is not None: + elapsed = time() - time_start + call_timeout = timeout-elapsed + continue + raise + for fd, mode in ready: + if mode & poll_in_or_priority_flags: + self.fill_rawq() + self.process_rawq() + for i in indices: + m = expect_list[i].search(self.cookedq) + if m: + e = m.end() + text = self.cookedq[:e] + self.cookedq = self.cookedq[e:] + break + if timeout is not None: + elapsed = time() - time_start + if elapsed >= timeout: + break + call_timeout = timeout-elapsed + poller.unregister(self) + if m: + return (i, m, text) + text = self.read_very_lazy() + if not text and self.eof: + raise EOFError + return (-1, None, text) + + def _expect_with_select(self, list, timeout=None): + """Read until one from a list of a regular expressions matches. + + The timeout is implemented using select.select(). + """ re = None list = list[:] indices = range(len(list)) |