def connect(self, protocolFactory):
        """
        Attempts a connection to each resolved address, and returns a
        connection which is established first.

        @param protocolFactory: The protocol factory whose protocol
            will be connected.
        @type protocolFactory:
            L{IProtocolFactory<twisted.internet.interfaces.IProtocolFactory>}

        @return: A L{Deferred} that fires with the connected protocol
            or fails a connection-related error.
        """
        if self._badHostname:
            return defer.fail(
                ValueError("invalid hostname: {}".format(self._hostStr))
            )

        d = Deferred()
        addresses = []
        @provider(IResolutionReceiver)
        class EndpointReceiver(object):
            @staticmethod
            def resolutionBegan(resolutionInProgress):
                pass
            @staticmethod
            def addressResolved(address):
                addresses.append(address)
            @staticmethod
            def resolutionComplete():
                d.callback(addresses)

        self._nameResolver.resolveHostName(
            EndpointReceiver, self._hostText, portNumber=self._port
        )

        d.addErrback(lambda ignored: defer.fail(error.DNSLookupError(
            "Couldn't find the hostname '{}'".format(self._hostStr))))
        @d.addCallback
        def resolvedAddressesToEndpoints(addresses):
            # Yield an endpoint for every address resolved from the name.
            for eachAddress in addresses:
                if isinstance(eachAddress, IPv6Address):
                    yield TCP6ClientEndpoint(
                        self._reactor, eachAddress.host, eachAddress.port,
                        self._timeout, self._bindAddress
                    )
                if isinstance(eachAddress, IPv4Address):
                    yield TCP4ClientEndpoint(
                        self._reactor, eachAddress.host, eachAddress.port,
                        self._timeout, self._bindAddress
                    )
        d.addCallback(list)

        def _canceller(d):
            # This canceller must remain defined outside of
            # `startConnectionAttempts`, because Deferred should not
            # participate in cycles with their cancellers; that would create a
            # potentially problematic circular reference and possibly
            # gc.garbage.
            d.errback(error.ConnectingCancelledError(
                HostnameAddress(self._hostBytes, self._port)))

        @d.addCallback
        def startConnectionAttempts(endpoints):
            """
            Given a sequence of endpoints obtained via name resolution, start
            connecting to a new one every C{self._attemptDelay} seconds until
            one of the connections succeeds, all of them fail, or the attempt
            is cancelled.

            @param endpoints: a list of all the endpoints we might try to
                connect to, as determined by name resolution.
            @type endpoints: L{list} of L{IStreamServerEndpoint}

            @return: a Deferred that fires with the result of the
                C{endpoint.connect} method that completes the fastest, or fails
                with the first connection error it encountered if none of them
                succeed.
            @rtype: L{Deferred} failing with L{error.ConnectingCancelledError}
                or firing with L{IProtocol}
            """
            if not endpoints:
                raise error.DNSLookupError(
                    "no results for hostname lookup: {}".format(self._hostStr)
                )
            iterEndpoints = iter(endpoints)
            pending = []
            failures = []
            winner = defer.Deferred(canceller=_canceller)

            def checkDone():
                if pending or checkDone.completed or checkDone.endpointsLeft:
                    return
                winner.errback(failures.pop())
            checkDone.completed = False
            checkDone.endpointsLeft = True

            @LoopingCall
            def iterateEndpoint():
                endpoint = next(iterEndpoints, None)
                if endpoint is None:
                    # The list of endpoints ends.
                    checkDone.endpointsLeft = False
                    checkDone()
                    return

                eachAttempt = endpoint.connect(protocolFactory)
                pending.append(eachAttempt)
                @eachAttempt.addBoth
                def noLongerPending(result):
                    pending.remove(eachAttempt)
                    return result
                @eachAttempt.addCallback
                def succeeded(result):
                    winner.callback(result)
                @eachAttempt.addErrback
                def failed(reason):
                    failures.append(reason)
                    checkDone()

            iterateEndpoint.clock = self._reactor
            iterateEndpoint.start(self._attemptDelay)

            @winner.addBoth
            def cancelRemainingPending(result):
                checkDone.completed = True
                for remaining in pending[:]:
                    remaining.cancel()
                if iterateEndpoint.running:
                    iterateEndpoint.stop()
                return result
            return winner

        return d

        Attempts a connection to each resolved address, and returns a
        connection which is established first.

        @param protocolFactory: The protocol factory whose protocol
            will be connected.
        @type protocolFactory:
            L{IProtocolFactory<twisted.internet.interfaces.IProtocolFactory>}

        @return: A L{Deferred} that fires with the connected protocol
            or fails a connection-related error.
        
stackoverflow/venv/lib/python3.6/site-packages/twisted/internet/endpoints.py