docs: def url_parse(url, scheme=None, allow_fragments=True):
      """Parses a URL from a string into a :class:`URL` tuple.  If the URL
      is lacking a scheme it can be provided as second argument. Otherwise,
      it is ignored.  Optionally fragments can be stripped from the URL
      by setting `allow_fragments` to `False`.
  
      The inverse of this function is :func:`url_unparse`.
  
      :param url: the URL to parse.
      :param scheme: the default schema to use if the URL is schemaless.
      :param allow_fragments: if set to `False` a fragment will be removed
                              from the URL.
      """
      s = make_literal_wrapper(url)
      is_text_based = isinstance(url, text_type)
  
      if scheme is None:
          scheme = s('')
      netloc = query = fragment = s('')
      i = url.find(s(':'))
      if i > 0 and _scheme_re.match(to_native(url[:i], errors='replace')):
          # make sure "iri" is not actually a port number (in which case
          # "scheme" is really part of the path)
          rest = url[i + 1:]
          if not rest or any(c not in s('0123456789') for c in rest):
              # not a port number
              scheme, url = url[:i].lower(), rest
  
      if url[:2] == s('//'):
          delim = len(url)
          for c in s('/?#'):
              wdelim = url.find(c, 2)
              if wdelim >= 0:
                  delim = min(delim, wdelim)
          netloc, url = url[2:delim], url[delim:]
          if ((s('[') in netloc and s(']') not in netloc) or
              (s(']') in netloc and s('[') not in netloc)):
              raise ValueError('Invalid IPv6 URL')
  
      if allow_fragments and s('#') in url:
          url, fragment = url.split(s('#'), 1)
      if s('?') in url:
          url, query = url.split(s('?'), 1)
  
      result_type = is_text_based and URL or BytesURL
      return result_type(scheme, netloc, url, query, fragment)