diff --git a/Doc/lib/libtelnetlib.tex b/Doc/lib/libtelnetlib.tex index c7a4226dccd..d2364984b1b 100644 --- a/Doc/lib/libtelnetlib.tex +++ b/Doc/lib/libtelnetlib.tex @@ -23,13 +23,16 @@ Mark), BRK (Break), IP (Interrupt process), AO (Abort output), AYT SB (Subnegotiation Begin). -\begin{classdesc}{Telnet}{\optional{host\optional{, port}}} +\begin{classdesc}{Telnet}{\optional{host\optional{, port\optional{, timeout}}}} \class{Telnet} represents a connection to a Telnet server. The instance is initially not connected by default; the \method{open()} method must be used to establish a connection. Alternatively, the host name and optional port number can be passed to the constructor, to, in which case the connection to the server will be established before the constructor returns. +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global default +timeout setting will be used). Do not reopen an already connected instance. @@ -111,10 +114,13 @@ The callback should access these data when it was invoked with a \versionadded{2.3} \end{methoddesc} -\begin{methoddesc}{open}{host\optional{, port}} +\begin{methoddesc}{open}{host\optional{, port\optional{, timeout}}} Connect to a host. The optional second argument is the port number, which defaults to the standard Telnet port (23). +The optional \var{timeout} parameter specifies a timeout in seconds for the +connection attempt (if not specified, or passed as None, the global default +timeout setting will be used). Do not try to reopen an already connected instance. \end{methoddesc} diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index a13e85cc986..f4c01e428cb 100644 --- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -184,7 +184,7 @@ class Telnet: """ - def __init__(self, host=None, port=0): + def __init__(self, host=None, port=0, timeout=None): """Constructor. When called without arguments, create an unconnected instance. @@ -195,6 +195,7 @@ class Telnet: self.debuglevel = DEBUGLEVEL self.host = host self.port = port + self.timeout = timeout self.sock = None self.rawq = '' self.irawq = 0 @@ -205,9 +206,9 @@ class Telnet: self.sbdataq = '' self.option_callback = None if host is not None: - self.open(host, port) + self.open(host, port, timeout) - def open(self, host, port=0): + def open(self, host, port=0, timeout=None): """Connect to a host. The optional second argument is the port number, which @@ -221,20 +222,9 @@ class Telnet: 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 - try: - self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) - except socket.error, msg: - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg + if timeout is not None: + self.timeout = timeout + self.sock = socket.create_connection((host, port), self.timeout) def __del__(self): """Destructor -- close the connection.""" @@ -661,7 +651,7 @@ def test(): port = socket.getservbyname(portstr, 'tcp') tn = Telnet() tn.set_debuglevel(debuglevel) - tn.open(host, port) + tn.open(host, port, timeout=0.5) tn.interact() tn.close() diff --git a/Lib/test/test_telnetlib.py b/Lib/test/test_telnetlib.py new file mode 100644 index 00000000000..42ad20cc5d9 --- /dev/null +++ b/Lib/test/test_telnetlib.py @@ -0,0 +1,74 @@ +import socket +import threading +import telnetlib +import time + +from unittest import TestCase +from test import test_support + + +def server(evt): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serv.settimeout(3) + serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + serv.bind(("", 9091)) + serv.listen(5) + try: + conn, addr = serv.accept() + except socket.timeout: + pass + finally: + serv.close() + evt.set() + +class GeneralTests(TestCase): + + def setUp(self): + self.evt = threading.Event() + threading.Thread(target=server, args=(self.evt,)).start() + time.sleep(.1) + + def tearDown(self): + self.evt.wait() + + def testBasic(self): + # connects + telnet = telnetlib.Telnet("localhost", 9091) + telnet.sock.close() + + def testTimeoutDefault(self): + # default + telnet = telnetlib.Telnet("localhost", 9091) + self.assertTrue(telnet.sock.gettimeout() is None) + telnet.sock.close() + + def testTimeoutValue(self): + # a value + telnet = telnetlib.Telnet("localhost", 9091, timeout=30) + self.assertEqual(telnet.sock.gettimeout(), 30) + telnet.sock.close() + + def testTimeoutDifferentOrder(self): + telnet = telnetlib.Telnet(timeout=30) + telnet.open("localhost", 9091) + self.assertEqual(telnet.sock.gettimeout(), 30) + telnet.sock.close() + + def testTimeoutNone(self): + # None, having other default + previous = socket.getdefaulttimeout() + socket.setdefaulttimeout(30) + try: + telnet = telnetlib.Telnet("localhost", 9091, timeout=None) + finally: + socket.setdefaulttimeout(previous) + self.assertEqual(telnet.sock.gettimeout(), 30) + telnet.sock.close() + + + +def test_main(verbose=None): + test_support.run_unittest(GeneralTests) + +if __name__ == '__main__': + test_main() diff --git a/Misc/NEWS b/Misc/NEWS index 3ebf16e8b30..e3dd20d01fd 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -200,6 +200,11 @@ Core and builtins Library ------- +- Added a timeout parameter to the constructor of other protocols + (telnetlib, ftplib, smtplib and poplib). This is second part of the + work started with create_connection() and timeout in httplib, and + closes patch #723312. + - Patch #1676823: Added create_connection() to socket.py, which may be called with a timeout, and use it from httplib (whose HTTPConnection now accepts an optional timeout).