Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
urls.py204 linesDownload Raw Back to werkzeug
1from __future__ import annotations2 3import codecs4import re5import typing as t6import urllib.parse7from urllib.parse import quote8from urllib.parse import unquote9from urllib.parse import urlencode10from urllib.parse import urlsplit11from urllib.parse import urlunsplit12 13from .datastructures import iter_multi_items14 15 16def _codec_error_url_quote(e: UnicodeError) -> tuple[str, int]:17    """Used in :func:`uri_to_iri` after unquoting to re-quote any18    invalid bytes.19    """20    # the docs state that UnicodeError does have these attributes,21    # but mypy isn't picking them up22    out = quote(e.object[e.start : e.end], safe="")  # type: ignore23    return out, e.end  # type: ignore24 25 26codecs.register_error("werkzeug.url_quote", _codec_error_url_quote)27 28 29def _make_unquote_part(name: str, chars: str) -> t.Callable[[str], str]:30    """Create a function that unquotes all percent encoded characters except those31    given. This allows working with unquoted characters if possible while not changing32    the meaning of a given part of a URL.33    """34    choices = "|".join(f"{ord(c):02X}" for c in sorted(chars))35    pattern = re.compile(f"((?:%(?:{choices}))+)", re.I)36 37    def _unquote_partial(value: str) -> str:38        parts = iter(pattern.split(value))39        out = []40 41        for part in parts:42            out.append(unquote(part, "utf-8", "werkzeug.url_quote"))43            out.append(next(parts, ""))44 45        return "".join(out)46 47    _unquote_partial.__name__ = f"_unquote_{name}"48    return _unquote_partial49 50 51# characters that should remain quoted in URL parts52# based on https://url.spec.whatwg.org/#percent-encoded-bytes53# always keep all controls, space, and % quoted54_always_unsafe = bytes((*range(0x21), 0x25, 0x7F)).decode()55_unquote_fragment = _make_unquote_part("fragment", _always_unsafe)56_unquote_query = _make_unquote_part("query", _always_unsafe + "&=+#")57_unquote_path = _make_unquote_part("path", _always_unsafe + "/?#")58_unquote_user = _make_unquote_part("user", _always_unsafe + ":@/?#")59 60 61def uri_to_iri(uri: str) -> str:62    """Convert a URI to an IRI. All valid UTF-8 characters are unquoted,63    leaving all reserved and invalid characters quoted. If the URL has64    a domain, it is decoded from Punycode.65 66    >>> uri_to_iri("http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF")67    'http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF'68 69    :param uri: The URI to convert.70 71    .. versionchanged:: 3.072        Passing a tuple or bytes, and the ``charset`` and ``errors`` parameters,73        are removed.74 75    .. versionchanged:: 2.376        Which characters remain quoted is specific to each part of the URL.77 78    .. versionchanged:: 0.1579        All reserved and invalid characters remain quoted. Previously,80        only some reserved characters were preserved, and invalid bytes81        were replaced instead of left quoted.82 83    .. versionadded:: 0.684    """85    parts = urlsplit(uri)86    path = _unquote_path(parts.path)87    query = _unquote_query(parts.query)88    fragment = _unquote_fragment(parts.fragment)89 90    if parts.hostname:91        netloc = _decode_idna(parts.hostname)92    else:93        netloc = ""94 95    if ":" in netloc:96        netloc = f"[{netloc}]"97 98    if parts.port:99        netloc = f"{netloc}:{parts.port}"100 101    if parts.username:102        auth = _unquote_user(parts.username)103 104        if parts.password:105            password = _unquote_user(parts.password)106            auth = f"{auth}:{password}"107 108        netloc = f"{auth}@{netloc}"109 110    return urlunsplit((parts.scheme, netloc, path, query, fragment))111 112 113def iri_to_uri(iri: str) -> str:114    """Convert an IRI to a URI. All non-ASCII and unsafe characters are115    quoted. If the URL has a domain, it is encoded to Punycode.116 117    >>> iri_to_uri('http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF')118    'http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF'119 120    :param iri: The IRI to convert.121 122    .. versionchanged:: 3.0123        Passing a tuple or bytes, the ``charset`` and ``errors`` parameters,124        and the ``safe_conversion`` parameter, are removed.125 126    .. versionchanged:: 2.3127        Which characters remain unquoted is specific to each part of the URL.128 129    .. versionchanged:: 0.15130        All reserved characters remain unquoted. Previously, only some reserved131        characters were left unquoted.132 133    .. versionchanged:: 0.9.6134       The ``safe_conversion`` parameter was added.135 136    .. versionadded:: 0.6137    """138    parts = urlsplit(iri)139    # safe = https://url.spec.whatwg.org/#url-path-segment-string140    # as well as percent for things that are already quoted141    path = quote(parts.path, safe="%!$&'()*+,/:;=@")142    query = quote(parts.query, safe="%!$&'()*+,/:;=?@")143    fragment = quote(parts.fragment, safe="%!#$&'()*+,/:;=?@")144 145    if parts.hostname:146        netloc = parts.hostname.encode("idna").decode("ascii")147    else:148        netloc = ""149 150    if ":" in netloc:151        netloc = f"[{netloc}]"152 153    if parts.port:154        netloc = f"{netloc}:{parts.port}"155 156    if parts.username:157        auth = quote(parts.username, safe="%!$&'()*+,;=")158 159        if parts.password:160            password = quote(parts.password, safe="%!$&'()*+,;=")161            auth = f"{auth}:{password}"162 163        netloc = f"{auth}@{netloc}"164 165    return urlunsplit((parts.scheme, netloc, path, query, fragment))166 167 168# Python < 3.12169# itms-services was worked around in previous iri_to_uri implementations, but170# we can tell Python directly that it needs to preserve the //.171if "itms-services" not in urllib.parse.uses_netloc:172    urllib.parse.uses_netloc.append("itms-services")173 174 175def _decode_idna(domain: str) -> str:176    try:177        data = domain.encode("ascii")178    except UnicodeEncodeError:179        # If the domain is not ASCII, it's decoded already.180        return domain181 182    try:183        # Try decoding in one shot.184        return data.decode("idna")185    except UnicodeDecodeError:186        pass187 188    # Decode each part separately, leaving invalid parts as punycode.189    parts = []190 191    for part in data.split(b"."):192        try:193            parts.append(part.decode("idna"))194        except UnicodeDecodeError:195            parts.append(part.decode("ascii"))196 197    return ".".join(parts)198 199 200def _urlencode(query: t.Mapping[str, str] | t.Iterable[tuple[str, str]]) -> str:201    items = [x for x in iter_multi_items(query) if x[1] is not None]202    # safe = https://url.spec.whatwg.org/#percent-encoded-bytes203    return urlencode(items, safe="!$'()*,/:;?@")204 
codekingpro/portable-devtools · Team Ai