Repository: encode/httpx
File: httpx/_urlparse.py (Python)

Code before:
    # with components that are plain ASCII bytestrings.
    parsed_scheme: str = scheme.lower()
    parsed_userinfo: str = quote(userinfo, safe=SUB_DELIMS + ":")
    parsed_host: str = encode_host(host)
    parsed_port: typing.Optional[int] = normalize_port(port, scheme)

    has_scheme = parsed_scheme != ""
    has_authority = (
        parsed_userinfo != "" or parsed_host != "" or parsed_port is not None
    )
    validate_path(path, has_scheme=has_scheme, has_authority=has_authority)
    if has_authority:
        path = normalize_path(path)

    # The GEN_DELIMS set is... : / ? # [ ] @
    # These do not need to be percent-quoted unless they serve as delimiters for the
    # specific component.

    # For 'path' we need to drop ? and # from the GEN_DELIMS set.
    parsed_path: str = quote(path, safe=SUB_DELIMS + ":/[]@")
    # For 'query' we need to drop '#' from the GEN_DELIMS set.
    # We also exclude '/' because it is more robust to replace it with a percent
    # encoding despite it not being a requirement of the spec and include '%' to
    # prevent duplicate encoding of previously quoted items.
    parsed_query: typing.Optional[str] = (
        None if query is None else quote(query, safe=SUB_DELIMS + ":?[]@%")
    )
    # For 'fragment' we can include all of the GEN_DELIMS set.
    parsed_fragment: typing.Optional[str] = (
        None if fragment is None else quote(fragment, safe=SUB_DELIMS + ":/?#[]@")
    )

    # The parsed ASCII bytestrings are our canonical form.
    # All properties of the URL are derived from these.
    return ParseResult(
        parsed_scheme,
        parsed_userinfo,
        parsed_host,
        parsed_port,
        parsed_path,
        parsed_query,
        parsed_fragment,
    )


def encode_host(host: str) -> str:
    if not host:
        return ""

    elif IPv4_STYLE_HOSTNAME.match(host):
        # Validate IPv4 hostnames like #.#.#.#

Review comment (suggestion):
I was surprised that I needed to include `%` but it was needed to to prevent spaces converted to `%20` from being changed to `%2520` in tests.

Code after:
    # with components that are plain ASCII bytestrings.
    parsed_scheme: str = scheme.lower()
    parsed_userinfo: str = quote(userinfo, safe=SUB_DELIMS + ":")
    parsed_host: str = encode_host(host)
    parsed_port: typing.Optional[int] = normalize_port(port, scheme)

    has_scheme = parsed_scheme != ""
    has_authority = (
        parsed_userinfo != "" or parsed_host != "" or parsed_port is not None
    )
    validate_path(path, has_scheme=has_scheme, has_authority=has_authority)
    if has_authority:
        path = normalize_path(path)

    # The GEN_DELIMS set is... : / ? # [ ] @
    # These do not need to be percent-quoted unless they serve as delimiters for the
    # specific component.

    # For 'path' we need to drop ? and # from the GEN_DELIMS set.
    parsed_path: str = quote(path, safe=SUB_DELIMS + ":/[]@")
    # For 'query' we need to drop '#' from the GEN_DELIMS set.
    # We also exclude '/' because it is more robust to replace it with a percent
    # encoding despite it not being a requirement of the spec.
    parsed_query: typing.Optional[str] = (
        None if query is None else quote(query, safe=SUB_DELIMS + ":?[]@")
    )
    # For 'fragment' we can include all of the GEN_DELIMS set.
    parsed_fragment: typing.Optional[str] = (
        None if fragment is None else quote(fragment, safe=SUB_DELIMS + ":/?#[]@")
    )

    # The parsed ASCII bytestrings are our canonical form.
    # All properties of the URL are derived from these.
    return ParseResult(
        parsed_scheme,
        parsed_userinfo,
        parsed_host,
        parsed_port,
        parsed_path,
        parsed_query,
        parsed_fragment,
    )


def encode_host(host: str) -> str:
    if not host:
        return ""

    elif IPv4_STYLE_HOSTNAME.match(host):
        # Validate IPv4 hostnames like #.#.#.#
        #