Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for gevent.socket

    # Copyright (c) 2009-2014 Denis Bilenko and gevent contributors. See LICENSE for details.

    """Cooperative low-level networking interface.

    This module provides socket operations and some related functions.
    The API of the functions and classes matches the API of the corresponding
    items in the standard :mod:`socket` module exactly, but the synchronous functions
    in this module only block the current greenlet and let the others run.

    For convenience, exceptions (like :class:`error <socket.error>` and :class:`timeout <socket.timeout>`)
    as well as the constants from the :mod:`socket` module are imported into this module.
    """
    # Our import magic sadly makes this warning useless
    # pylint: disable=undefined-variable

    from gevent._compat import PY311
    from gevent._compat import exc_clear
    from gevent._util import copy_globals

    from gevent import _socket3 as _source

    # define some things we're expecting to overwrite; each module
    # needs to define these
    __implements__ = __dns__ = __all__ = __extensions__ = __imports__ = ()

    class error(Exception):
        errno = None

    def getfqdn(*args):
        # pylint:disable=unused-argument
        raise NotImplementedError()

    copy_globals(_source, globals(),
                 dunder_names_to_keep=('__implements__', '__dns__', '__all__',
                                       '__extensions__', '__imports__', '__socket__'),
                 cleanup_globs=False)

    # The _socket2 and _socket3 don't import things defined in
    # __extensions__, to help avoid confusing reference cycles in the
    # documentation and to prevent importing from the wrong place, but we
    # *do* need to expose them here. (NOTE: This may lead to some sphinx
    # warnings like:
    #    WARNING: missing attribute mentioned in :members: or __all__:
    #             module gevent._socket2, attribute cancel_wait
    # These can be ignored.)
    from gevent import _socketcommon
    copy_globals(_socketcommon, globals(),
                 only_names=_socketcommon.__extensions__)

    try:
        _GLOBAL_DEFAULT_TIMEOUT = __socket__._GLOBAL_DEFAULT_TIMEOUT
    except AttributeError:
        _GLOBAL_DEFAULT_TIMEOUT = object()

    def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None, *,
                          all_errors=False):
        """
        create_connection(address, timeout=None, source_address=None, *, all_errors=False) -> 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
        *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.

        .. versionchanged:: 20.6.0
            If the host part of the address includes an IPv6 scope ID,
            it will be used instead of ignored, if the platform supplies
            :func:`socket.inet_pton`.
        .. versionchanged:: 22.08.0
            Add the *all_errors* argument. This only has meaning on Python 3.11+;
            it is a programming error to pass it on earlier versions.
        .. versionchanged:: 23.7.0
            You can pass a value for ``all_errors`` on any version of Python.
            It is forced to false for any version before 3.11 inside the function.
        """
        # Sigh. This function is a near-copy of the CPython implementation.
        # Even though we simplified some things, it's still a little complex to
        # cope with error handling, which got even more complicated in 3.11.
        # pylint:disable=too-many-locals,too-many-branches
        if not PY311:
            all_errors = False

        host, port = address
        exceptions = []
        # 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, _canonname, sa = res
            sock = None
            try:
                sock = socket(af, socktype, proto)
                if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
                    sock.settimeout(timeout)
                if source_address:
                    sock.bind(source_address)
                sock.connect(sa)

            except error as exc:
                if not all_errors:
                    exceptions = [exc] # raise only the last error
                else:
                    exceptions.append(exc)
                del exc # cycle
                if sock is not None:
                    sock.close()
                sock = None
                if res is addrs[-1]:
                    if not all_errors:
                        del exceptions[:]
                        raise
                    try:
                        # pylint isn't smart enough to see that we only use this
                        # on supported versions.
                        # pylint:disable=using-exception-groups-in-unsupported-version
                        raise ExceptionGroup("create_connection failed", exceptions)
                    finally:
                        # Break explicitly a reference cycle
                        del exceptions[:]
                # 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:
                # break reference cycles
                del exceptions[:]
                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
    # in the _source too.
    _source.create_connection = create_connection