mirror of
https://github.com/python/cpython.git
synced 2025-08-23 02:04:56 +00:00
Fixes Issue #14635: telnetlib will use poll() rather than select() when possible
to avoid failing due to the select() file descriptor limit.
This commit is contained in:
parent
4774946c3b
commit
dad5711677
4 changed files with 223 additions and 7 deletions
130
Lib/telnetlib.py
130
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)
|
||||
|
||||
|
@ -286,6 +288,61 @@ class Telnet:
|
|||
possibly the empty string. Raise EOFError if the connection
|
||||
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()
|
||||
|
@ -588,6 +645,79 @@ class Telnet:
|
|||
or if more than one expression can match the same input, the
|
||||
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[:]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue