--- telnetlib.py.orig 2005-11-18 15:57:02.918274576 +0000 +++ telnetlib.py 2005-11-18 15:56:45.845869976 +0000 @@ -37,6 +37,7 @@ import sys import socket import select +import time __all__ = ["Telnet"] @@ -207,39 +208,90 @@ if host is not None: self.open(host, port) - def open(self, host, port=0): - """Connect to a host. + def open(self, host, port=0, timeout=None, total_timeout=None): + """open(host, port=23, timeout=None, total_timeout=None) + + Connect to a host. The optional second argument is the port number, which defaults to the standard telnet port (23). - Don't try to reopen an already connected instance. + The timeout argument is the number of seconds a connect + attempt can block for. + In the case of a host with multiple addresses, the addresses + are tried in turn until one can be connected to. The + total_timeout argument specifies how long connection attempts + may be made for. If total_timeout > timeout, more than one + address may be tried and timed out. If total_timeout < timout + or is not specified, a timeout connecting to the first address + will timeout the whole open() call. + + Don't try to reopen an already connected instance. """ + if total_timeout is not None: + deadline = time.time() + total_timeout + if timeout is None or timeout > total_timeout: + timeout = total_timeout + elif timeout is not None: + deadline = time.time() + timeout + else: + deadline = None + self.eof = 0 if not port: port = TELNET_PORT self.host = host self.port = port - msg = "getaddrinfo returns an empty list" - for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res + + addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM) + if not addrs: + raise socket.error("getaddrinfo returns an empty list") + + for af, socktype, proto, canonname, sa in addrs: try: self.sock = socket.socket(af, socktype, proto) + + except socket.error, exc: + continue + + if deadline is not None: + t = deadline - time.time() + if t <= 0.0: + raise socket.timeout("Connect deadline reached.") + self.sock.settimeout(min(t, timeout)) + + try: self.sock.connect(sa) - except socket.error, msg: + self.sock.settimeout(None) + + except socket.error, exc: if self.sock: self.sock.close() self.sock = None continue + break + if not self.sock: - raise socket.error, msg + raise exc def __del__(self): """Destructor -- close the connection.""" self.close() + def settimeout(self, timeout): + """Set the timeout for individual socket sends and receives. + + A connection must have been established for this to have any + effect. + + This value is also used if no timeout is specified in + read_until() and expect(). + """ + if self.sock is not None: + self.sock.settimeout(timeout) + def msg(self, msg, *args): """Print a debug message, when the debug level is > 0. @@ -309,9 +361,25 @@ return buf s_reply = ([self], [], []) s_args = s_reply + + if timeout is None: + timeout = self.sock.gettimeout() + if timeout is not None: - s_args = s_args + (timeout,) - while not self.eof and select.select(*s_args) == s_reply: + deadline = time.time() + timeout + else: + deadline = None + + while not self.eof: + if deadline is not None: + t = deadline - time.time() + if t <= 0.0: + break + s_args = s_reply + (t,) + + if select.select(*s_args) != s_reply: + break + i = max(0, len(self.cookedq)-n) self.fill_rawq() self.process_rawq() @@ -601,6 +669,15 @@ if not hasattr(list[i], "search"): if not re: import re list[i] = re.compile(list[i]) + + if timeout is None: + timeout = self.sock.gettimeout() + + if timeout is not None: + deadline = time.time() + timeout + else: + deadline = None + while 1: self.process_rawq() for i in indices: @@ -612,8 +689,11 @@ return (i, m, text) if self.eof: break - if timeout is not None: - r, w, x = select.select([self.fileno()], [], [], timeout) + if deadline is not None: + t = deadline - time.time() + if t <= 0.0: + break + r, w, x = select.select([self.fileno()], [], [], t) if not r: break self.fill_rawq()