summaryrefslogtreecommitdiffstats
path: root/Lib/telnetlib.py
diff options
context:
space:
mode:
authorGregory P. Smith <greg@krypto.org>2012-07-16 06:42:26 (GMT)
committerGregory P. Smith <greg@krypto.org>2012-07-16 06:42:26 (GMT)
commitdad5711677f965a62713bfc9a34599c8f44572e8 (patch)
tree336b01e8761c7ff2baecefa3cc67f49a6e3a8245 /Lib/telnetlib.py
parent4774946c3b7564b4a1c93da4ba4dba443a36a708 (diff)
downloadcpython-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.py130
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))