diff options
Diffstat (limited to 'python/gevent/socket.py')
-rw-r--r-- | python/gevent/socket.py | 71 |
1 files changed, 47 insertions, 24 deletions
diff --git a/python/gevent/socket.py b/python/gevent/socket.py index 50f7a59..1bb039e 100644 --- a/python/gevent/socket.py +++ b/python/gevent/socket.py @@ -13,8 +13,8 @@ as well as the constants from the :mod:`socket` module are imported into this mo # Our import magic sadly makes this warning useless # pylint: disable=undefined-variable -import sys from gevent._compat import PY3 +from gevent._compat import exc_clear from gevent._util import copy_globals @@ -60,21 +60,31 @@ except AttributeError: def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None): - """Connect to *address* and return the socket object. + """ + create_connection(address, timeout=None, source_address=None) -> socket + + Connect to *address* and return the :class:`gevent.socket.socket` + object. - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` - is used. If *source_address* is set it must be a tuple of (host, port) - for the socket to bind as a source address before making the connection. - A host of '' or port 0 tells the OS to use the default. + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by + :func:`getdefaulttimeout` is used. If *source_address* is set it + must be a tuple of (host, port) for the socket to bind as a source + address before making the connection. A host of '' or port 0 tells + the OS to use the default. """ host, port = address - err = None - for res in getaddrinfo(host, port, 0 if has_ipv6 else AF_INET, SOCK_STREAM): + # getaddrinfo is documented as returning a list, but our interface + # is pluggable, so be sure it does. + addrs = list(getaddrinfo(host, port, 0, SOCK_STREAM)) + if not addrs: + raise error("getaddrinfo returns an empty list") + + for res in addrs: af, socktype, proto, _, sa = res sock = None try: @@ -84,21 +94,34 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=N if source_address: sock.bind(source_address) sock.connect(sa) - return sock - except error as ex: - # without exc_clear(), if connect() fails once, the socket is referenced by the frame in exc_info - # and the next bind() fails (see test__socket.TestCreateConnection) - # that does not happen with regular sockets though, because _socket.socket.connect() is a built-in. - # this is similar to "getnameinfo loses a reference" failure in test_socket.py - if not PY3: - sys.exc_clear() # pylint:disable=no-member,useless-suppression + except error: if sock is not None: sock.close() - err = ex - if err is not None: - raise err # pylint:disable=raising-bad-type - else: - raise error("getaddrinfo returns an empty list") + sock = None + if res is addrs[-1]: + raise + # without exc_clear(), if connect() fails once, the socket + # is referenced by the frame in exc_info and the next + # bind() fails (see test__socket.TestCreateConnection) + # that does not happen with regular sockets though, + # because _socket.socket.connect() is a built-in. this is + # similar to "getnameinfo loses a reference" failure in + # test_socket.py + exc_clear() + except BaseException: + # Things like GreenletExit, Timeout and KeyboardInterrupt. + # These get raised immediately, being sure to + # close the socket + if sock is not None: + sock.close() + sock = None + raise + else: + try: + return sock + finally: + sock = None + # This is promised to be in the __all__ of the _source, but, for circularity reasons, # we implement it in this module. Mostly for documentation purposes, put it |