# Path: src/werkzeug/_internal.py
# def _check_str_tuple(value: t.Tuple[t.AnyStr, ...]) -> None:
#     """Ensure tuple items are all strings or all bytes."""
#     if not value:
#         return
# 
#     item_type = str if isinstance(value[0], str) else bytes
# 
#     if any(not isinstance(item, item_type) for item in value):
#         raise TypeError(f"Cannot mix str and bytes arguments (got {value!r})")
# 
# Path: src/werkzeug/_internal.py
# def _decode_idna(domain: t.Union[str, bytes]) -> str:
#     # If the input is a string try to encode it to ascii to do the idna
#     # decoding. If that fails because of a unicode error, then we
#     # already have a decoded idna domain.
#     if isinstance(domain, str):
#         try:
#             domain = domain.encode("ascii")
#         except UnicodeError:
#             return domain  # type: ignore
# 
#     # Decode each part separately. If a part fails, try to decode it
#     # with ascii and silently ignore errors. This makes sense because
#     # the idna codec does not have error handling.
#     def decode_part(part: bytes) -> str:
#         try:
#             return part.decode("idna")
#         except UnicodeError:
#             return part.decode("ascii", "ignore")
# 
#     return ".".join(decode_part(p) for p in domain.split(b"."))
# 
# Path: src/werkzeug/_internal.py
# def _encode_idna(domain: str) -> bytes:
#     # If we're given bytes, make sure they fit into ASCII
#     if isinstance(domain, bytes):
#         domain.decode("ascii")
#         return domain
# 
#     # Otherwise check if it's already ascii, then return
#     try:
#         return domain.encode("ascii")
#     except UnicodeError:
#         pass
# 
#     # Otherwise encode each part separately
#     return b".".join(p.encode("idna") for p in domain.split("."))
# 
# Path: src/werkzeug/_internal.py
# @typing.overload
# def _make_encode_wrapper(reference: str) -> t.Callable[[str], str]:
#     ...
# 
# Path: src/werkzeug/_internal.py
# @typing.overload
# def _to_str(  # type: ignore
#     x: None,
#     charset: t.Optional[str] = ...,
#     errors: str = ...,
#     allow_none_charset: bool = ...,
# ) -> None:
#     ...


import codecs
import os
import re
import typing as t
            import ntpath
            import posixpath
from ._internal import _check_str_tuple
from ._internal import _decode_idna
from ._internal import _encode_idna
from ._internal import _make_encode_wrapper
from ._internal import _to_str
    from . import datastructures as ds
    from .datastructures import iter_multi_items
        from .datastructures import MultiDict  # noqa: F811
    from .wsgi import make_chunk_iter
        from .datastructures import MultiDict  # noqa: F811
    for char in bytearray(string):
        if char in safe:
            rv.append(char)
        else:
            rv.extend(_bytetohex[char])
    return bytes(rv).decode(charset)


def url_quote_plus(
    string: str, charset: str = "utf-8", errors: str = "strict", safe: str = ""
) -> str:
    """URL encode a single string with the given encoding and convert
    whitespace to "+".

    :param s: The string to quote.
    :param charset: The charset to be used.
    :param safe: An optional sequence of safe characters.
    """
    return url_quote(string, charset, errors, safe + " ", "+").replace(" ", "+")


def url_unparse(components: t.Tuple[str, str, str, str, str]) -> str:
    """The reverse operation to :meth:`url_parse`.  This accepts arbitrary
    as well as :class:`URL` tuples and returns a URL as a string.

    :param components: the parsed URL as tuple which should be converted
                       into a URL string.
    """
    _check_str_tuple(components)
    scheme, netloc, path, query, fragment = components

# Path: src/werkzeug/_internal.py
# def _check_str_tuple(value: t.Tuple[t.AnyStr, ...]) -> None:
#     """Ensure tuple items are all strings or all bytes."""
#     if not value:
#         return
# 
#     item_type = str if isinstance(value[0], str) else bytes
# 
#     if any(not isinstance(item, item_type) for item in value):
#         raise TypeError(f"Cannot mix str and bytes arguments (got {value!r})")
# 
# Path: src/werkzeug/_internal.py
# def _decode_idna(domain: t.Union[str, bytes]) -> str:
#     # If the input is a string try to encode it to ascii to do the idna
#     # decoding. If that fails because of a unicode error, then we
#     # already have a decoded idna domain.
#     if isinstance(domain, str):
#         try:
#             domain = domain.encode("ascii")
#         except UnicodeError:
#             return domain  # type: ignore
# 
#     # Decode each part separately. If a part fails, try to decode it
#     # with ascii and silently ignore errors. This makes sense because
#     # the idna codec does not have error handling.
#     def decode_part(part: bytes) -> str:
#         try:
#             return part.decode("idna")
#         except UnicodeError:
#             return part.decode("ascii", "ignore")
# 
#     return ".".join(decode_part(p) for p in domain.split(b"."))
# 
# Path: src/werkzeug/_internal.py
# def _encode_idna(domain: str) -> bytes:
#     # If we're given bytes, make sure they fit into ASCII
#     if isinstance(domain, bytes):
#         domain.decode("ascii")
#         return domain
# 
#     # Otherwise check if it's already ascii, then return
#     try:
#         return domain.encode("ascii")
#     except UnicodeError:
#         pass
# 
#     # Otherwise encode each part separately
#     return b".".join(p.encode("idna") for p in domain.split("."))
# 
# Path: src/werkzeug/_internal.py
# @typing.overload
# def _make_encode_wrapper(reference: str) -> t.Callable[[str], str]:
#     ...
# 
# Path: src/werkzeug/_internal.py
# @typing.overload
# def _to_str(  # type: ignore
#     x: None,
#     charset: t.Optional[str] = ...,
#     errors: str = ...,
#     allow_none_charset: bool = ...,
# ) -> None:
#     ...


# Path: src/werkzeug/urls.py
import codecs
import os
import re
import typing as t
            import ntpath
            import posixpath
from ._internal import _check_str_tuple
from ._internal import _decode_idna
from ._internal import _encode_idna
from ._internal import _make_encode_wrapper
from ._internal import _to_str
    from . import datastructures as ds
    from .datastructures import iter_multi_items
        from .datastructures import MultiDict  # noqa: F811
    from .wsgi import make_chunk_iter
        from .datastructures import MultiDict  # noqa: F811

    for char in bytearray(string):
        if char in safe:
            rv.append(char)
        else:
            rv.extend(_bytetohex[char])
    return bytes(rv).decode(charset)


def url_quote_plus(
    string: str, charset: str = "utf-8", errors: str = "strict", safe: str = ""
) -> str:
    """URL encode a single string with the given encoding and convert
    whitespace to "+".

    :param s: The string to quote.
    :param charset: The charset to be used.
    :param safe: An optional sequence of safe characters.
    """
    return url_quote(string, charset, errors, safe + " ", "+").replace(" ", "+")


def url_unparse(components: t.Tuple[str, str, str, str, str]) -> str:
    """The reverse operation to :meth:`url_parse`.  This accepts arbitrary
    as well as :class:`URL` tuples and returns a URL as a string.

    :param components: the parsed URL as tuple which should be converted
                       into a URL string.
    """
    _check_str_tuple(components)
    scheme, netloc, path, query, fragment = components

    s = _make_encode_wrapper(scheme)