docs: def detect_ip(kind):
      """
      Detect IP address.
  
      kind can be:
          IPV4 - returns IPv4 address
          IPV6_ANY - returns any IPv6 address (no preference)
          IPV6_PUBLIC - returns public IPv6 address
          IPV6_TMP - returns temporary IPV6 address (privacy extensions)
  
      This function either returns an IP address (str) or
      raises a GetIpException.
      """
      if kind not in (IPV4, IPV6_PUBLIC, IPV6_TMP, IPV6_ANY):
          raise ValueError("invalid kind specified")
  
      # We create an UDP socket and connect it to a public host.
      # We query the OS to know what our address is.
      # No packet will really be sent since we are using UDP.
      af = socket.AF_INET if kind == IPV4 else socket.AF_INET6
      s = socket.socket(af, socket.SOCK_DGRAM)
      try:
          if kind in [IPV6_PUBLIC, IPV6_TMP, ]:
              # caller wants some specific kind of IPv6 address (not IPV6_ANY)
              try:
                  if kind == IPV6_PUBLIC:
                      preference = socket.IPV6_PREFER_SRC_PUBLIC
                  elif kind == IPV6_TMP:
                      preference = socket.IPV6_PREFER_SRC_TMP
                  s.setsockopt(socket.IPPROTO_IPV6,
                               socket.IPV6_ADDR_PREFERENCES, preference)
              except socket.error as e:
                  if e.errno == errno.ENOPROTOOPT:
                      raise GetIpException("Kernel doesn't support IPv6 address preference")
                  else:
                      raise GetIpException("Unable to set IPv6 address preference: %s" % e)
  
          try:
              outside_ip = OUTSIDE_IPV4 if kind == IPV4 else OUTSIDE_IPV6
              s.connect((outside_ip, 9))
          except (socket.error, socket.gaierror) as e:
              raise GetIpException(str(e))
  
          ip = s.getsockname()[0]
      finally:
          s.close()
      return ip