docs: 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)