def create_connection(protocol_factory, address, ssl=False, server_hostname=None,
                      local_address=None, family=0, flags=0, ipc=False, timeout=None,
                      mode='rw'):
    """Create a new client connection.

    This method creates a new :class:`pyuv.Handle`, connects it to *address*,
    and then waits for the connection to be established. When the connection is
    established, the handle is wrapped in a transport, and a new protocol
    instance is created by calling *protocol_factory*. The protocol is then
    connected to the transport by calling the transport's
    :meth:`~BaseTransport.start` method which in turn calls
    :meth:`~BaseProtocol.connection_made` on the protocol. Finally the results
    are returned as a ``(transport, protocol)`` tuple.

    The address may be either be a string, a (host, port) tuple, a
    ``pyuv.Stream`` handle or a file descriptor:

    * If the address is a string, this method connects to a named pipe using a
      :class:`pyuv.Pipe` handle. The address specifies the pipe name.
    * If the address is a tuple, this method connects to a TCP/IP service using
      a :class:`pyuv.TCP` handle. The first element of the tuple specifies the
      IP address or DNS name, and the second element specifies the port number
      or service name.
    * If the address is a ``pyuv.Stream`` instance, it must be an already
      connected stream.
    * If the address is a file descriptor, then it is attached to a
      :class:`pyuv.TTY` stream if :func:`os.isatty` returns true, or to a
      :class:`pyuv.Pipe` instance otherwise.

    The *ssl* parameter indicates whether an SSL/TLS connection is desired. If
    so then an :class:`ssl.SSLContext` instance is used to wrap the connection
    using the :mod:`ssl` module's asynchronous SSL support. The context is
    created as follows. If *ssl* is an :class:`~ssl.SSLContext` instance, it is
    used directly. If it is a function, it is called with the connection handle
    as an argument and it must return a context . If it is ``True`` then a
    default context is created using :func:`gruvi.create_default_context`. To
    disable SSL (the default), pass ``False``. If SSL is active, the return
    transport will be an :class:`SslTransport` instance, otherwise it will be a
    :class:`Transport` instance.

    The *server_hostname* parameter is only relevant for SSL connections, and
    specifies the server hostname to use with SNI (Server Name Indication). If
    no server hostname is provided, the hostname specified in *address* is
    used, if available.

    The *local_address* keyword argument is relevant only for TCP transports.
    If provided, it specifies the local address to bind to.

    The *family* and *flags* keyword arguments are used to customize address
    resolution for TCP handles as described in :func:`socket.getaddrinfo`.

    The *mode* parameter specifies if the transport should be put in read-only
    (``'r'``), write-only (``'w'``) or read-write (``'rw'``) mode. For TTY
    transports, the mode must be either read-only or write-only. For all other
    transport the mode should usually be read-write.
    """
    hub = get_hub()
    log = logging.get_logger()
    handle_args = ()
    if isinstance(address, (six.binary_type, six.text_type)):
        handle_type = pyuv.Pipe
        handle_args = (ipc,)
        addresses = [address]
    elif isinstance(address, tuple):
        handle_type = pyuv.TCP
        result = getaddrinfo(address[0], address[1], family, socket.SOCK_STREAM,
                             socket.IPPROTO_TCP, flags)
        addresses = [res[4] for res in result]
        if server_hostname is None:
            server_hostname = address[0]
            # Python 2.7 annoyingly gives a unicode IP address
            if not isinstance(server_hostname, str):
                server_hostname = server_hostname.encode('ascii')
    elif isinstance(address, int):
        if os.isatty(address):
            if mode not in ('r', 'w'):
                raise ValueError("mode: must be either 'r' or 'w' for tty")
            handle = pyuv.TTY(hub.loop, address, mode == 'r')
        else:
            handle = pyuv.Pipe(hub.loop, ipc)
        handle.open(address)
        addresses = []; error = None
    elif isinstance(address, pyuv.Stream):
        handle = address
        addresses = []; error = None
    else:
        raise TypeError('expecting a string, tuple, fd, or pyuv.Stream')
    for addr in addresses:
        log.debug('trying address {}', saddr(addr))
        handle = handle_type(hub.loop, *handle_args)
        error = None
        try:
            if compat.pyuv_pipe_helper(handle, handle_args, 'connect', addr):
                break
            with switch_back(timeout) as switcher:
                handle.connect(addr, switcher)
                result = hub.switch()
                _, error = result[0]
        except pyuv.error.UVError as e:
            error = e[0]
        except Timeout:
            error = pyuv.errno.UV_ETIMEDOUT
        if not error:
            break
        handle.close()
        log.warning('connect() failed with error {}', error)
    if error:
        log.warning('all addresses failed')
        raise TransportError.from_errno(error)
    if local_address:
        handle.bind(*local_address)
    protocol = protocol_factory()
    protocol._timeout = timeout
    if ssl:
        context = ssl if hasattr(ssl, 'set_ciphers') \
                        else ssl(handle) if callable(ssl) \
                        else create_default_context(False)
        transport = SslTransport(handle, context, False, server_hostname)
    else:
        transport = Transport(handle, server_hostname, mode)
    events = transport.start(protocol)
    if events:
        for event in events:
            event.wait()
        transport._check_status()
    return (transport, protocol)
Create a new client connection.

    This method creates a new :class:`pyuv.Handle`, connects it to *address*,
    and then waits for the connection to be established. When the connection is
    established, the handle is wrapped in a transport, and a new protocol
    instance is created by calling *protocol_factory*. The protocol is then
    connected to the transport by calling the transport's
    :meth:`~BaseTransport.start` method which in turn calls
    :meth:`~BaseProtocol.connection_made` on the protocol. Finally the results
    are returned as a ``(transport, protocol)`` tuple.

    The address may be either be a string, a (host, port) tuple, a
    ``pyuv.Stream`` handle or a file descriptor:

    * If the address is a string, this method connects to a named pipe using a
      :class:`pyuv.Pipe` handle. The address specifies the pipe name.
    * If the address is a tuple, this method connects to a TCP/IP service using
      a :class:`pyuv.TCP` handle. The first element of the tuple specifies the
      IP address or DNS name, and the second element specifies the port number
      or service name.
    * If the address is a ``pyuv.Stream`` instance, it must be an already
      connected stream.
    * If the address is a file descriptor, then it is attached to a
      :class:`pyuv.TTY` stream if :func:`os.isatty` returns true, or to a
      :class:`pyuv.Pipe` instance otherwise.

    The *ssl* parameter indicates whether an SSL/TLS connection is desired. If
    so then an :class:`ssl.SSLContext` instance is used to wrap the connection
    using the :mod:`ssl` module's asynchronous SSL support. The context is
    created as follows. If *ssl* is an :class:`~ssl.SSLContext` instance, it is
    used directly. If it is a function, it is called with the connection handle
    as an argument and it must return a context . If it is ``True`` then a
    default context is created using :func:`gruvi.create_default_context`. To
    disable SSL (the default), pass ``False``. If SSL is active, the return
    transport will be an :class:`SslTransport` instance, otherwise it will be a
    :class:`Transport` instance.

    The *server_hostname* parameter is only relevant for SSL connections, and
    specifies the server hostname to use with SNI (Server Name Indication). If
    no server hostname is provided, the hostname specified in *address* is
    used, if available.

    The *local_address* keyword argument is relevant only for TCP transports.
    If provided, it specifies the local address to bind to.

    The *family* and *flags* keyword arguments are used to customize address
    resolution for TCP handles as described in :func:`socket.getaddrinfo`.

    The *mode* parameter specifies if the transport should be put in read-only
    (``'r'``), write-only (``'w'``) or read-write (``'rw'``) mode. For TTY
    transports, the mode must be either read-only or write-only. For all other
    transport the mode should usually be read-write.