Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_quoting_py.py214 linesDownload Raw Back to yarl
1import codecs2import re3from string import ascii_letters, ascii_lowercase, digits4from typing import overload5 6BASCII_LOWERCASE = ascii_lowercase.encode("ascii")7BPCT_ALLOWED = {f"%{i:02X}".encode("ascii") for i in range(256)}8GEN_DELIMS = ":/?#[]@"9SUB_DELIMS_WITHOUT_QS = "!$'()*,"10SUB_DELIMS = SUB_DELIMS_WITHOUT_QS + "+&=;"11RESERVED = GEN_DELIMS + SUB_DELIMS12UNRESERVED = ascii_letters + digits + "-._~"13ALLOWED = UNRESERVED + SUB_DELIMS_WITHOUT_QS14 15 16_IS_HEX = re.compile(b"[A-Z0-9][A-Z0-9]")17_IS_HEX_STR = re.compile("[A-Fa-f0-9][A-Fa-f0-9]")18 19utf8_decoder = codecs.getincrementaldecoder("utf-8")20 21 22class _Quoter:23    def __init__(24        self,25        *,26        safe: str = "",27        protected: str = "",28        qs: bool = False,29        requote: bool = True,30    ) -> None:31        self._safe = safe32        self._protected = protected33        self._qs = qs34        self._requote = requote35 36    @overload37    def __call__(self, val: str) -> str: ...38    @overload39    def __call__(self, val: None) -> None: ...40    def __call__(self, val: str | None) -> str | None:41        if val is None:42            return None43        if not isinstance(val, str):44            raise TypeError("Argument should be str")45        if not val:46            return ""47        bval = val.encode("utf8", errors="ignore")48        ret = bytearray()49        pct = bytearray()50        safe = self._safe51        safe += ALLOWED52        if not self._qs:53            safe += "+&=;"54        safe += self._protected55        bsafe = safe.encode("ascii")56        idx = 057        while idx < len(bval):58            ch = bval[idx]59            idx += 160 61            if pct:62                if ch in BASCII_LOWERCASE:63                    ch = ch - 32  # convert to uppercase64                pct.append(ch)65                if len(pct) == 3:  # pragma: no branch   # peephole optimizer66                    buf = pct[1:]67                    if not _IS_HEX.match(buf):68                        ret.extend(b"%25")69                        pct.clear()70                        idx -= 271                        continue72                    try:73                        unquoted = chr(int(pct[1:].decode("ascii"), base=16))74                    except ValueError:75                        ret.extend(b"%25")76                        pct.clear()77                        idx -= 278                        continue79 80                    if unquoted in self._protected:81                        ret.extend(pct)82                    elif unquoted in safe:83                        ret.append(ord(unquoted))84                    else:85                        ret.extend(pct)86                    pct.clear()87 88                # special case, if we have only one char after "%"89                elif len(pct) == 2 and idx == len(bval):90                    ret.extend(b"%25")91                    pct.clear()92                    idx -= 193 94                continue95 96            elif ch == ord("%") and self._requote:97                pct.clear()98                pct.append(ch)99 100                # special case if "%" is last char101                if idx == len(bval):102                    ret.extend(b"%25")103 104                continue105 106            if self._qs and ch == ord(" "):107                ret.append(ord("+"))108                continue109            if ch in bsafe:110                ret.append(ch)111                continue112 113            ret.extend((f"%{ch:02X}").encode("ascii"))114 115        ret2 = ret.decode("ascii")116        if ret2 == val:117            return val118        return ret2119 120 121class _Unquoter:122    def __init__(123        self,124        *,125        ignore: str = "",126        unsafe: str = "",127        qs: bool = False,128        plus: bool = False,129    ) -> None:130        self._ignore = ignore131        self._unsafe = unsafe132        self._qs = qs133        self._plus = plus  # to match urllib.parse.unquote_plus134        self._quoter = _Quoter()135        self._qs_quoter = _Quoter(qs=True)136 137    @overload138    def __call__(self, val: str) -> str: ...139    @overload140    def __call__(self, val: None) -> None: ...141    def __call__(self, val: str | None) -> str | None:142        if val is None:143            return None144        if not isinstance(val, str):145            raise TypeError("Argument should be str")146        if not val:147            return ""148        decoder = utf8_decoder()149        ret = []150        idx = 0151        while idx < len(val):152            ch = val[idx]153            idx += 1154            if ch == "%" and idx <= len(val) - 2:155                pct = val[idx : idx + 2]156                if _IS_HEX_STR.fullmatch(pct):157                    b = bytes([int(pct, base=16)])158                    idx += 2159                    try:160                        unquoted = decoder.decode(b)161                    except UnicodeDecodeError:162                        start_pct = idx - 3 - len(decoder.buffer) * 3163                        ret.append(val[start_pct : idx - 3])164                        decoder.reset()165                        try:166                            unquoted = decoder.decode(b)167                        except UnicodeDecodeError:168                            ret.append(val[idx - 3 : idx])169                            continue170                    if not unquoted:171                        continue172                    if self._qs and unquoted in "+=&;":173                        to_add = self._qs_quoter(unquoted)174                        if to_add is None:  # pragma: no cover175                            raise RuntimeError("Cannot quote None")176                        ret.append(to_add)177                    elif unquoted in self._unsafe or unquoted in self._ignore:178                        to_add = self._quoter(unquoted)179                        if to_add is None:  # pragma: no cover180                            raise RuntimeError("Cannot quote None")181                        ret.append(to_add)182                    else:183                        ret.append(unquoted)184                    continue185 186            if decoder.buffer:187                start_pct = idx - 1 - len(decoder.buffer) * 3188                ret.append(val[start_pct : idx - 1])189                decoder.reset()190 191            if ch == "+":192                if (not self._qs and not self._plus) or ch in self._unsafe:193                    ret.append("+")194                else:195                    ret.append(" ")196                continue197 198            if ch in self._unsafe:199                ret.append("%")200                h = hex(ord(ch)).upper()[2:]201                for ch in h:202                    ret.append(ch)203                continue204 205            ret.append(ch)206 207        if decoder.buffer:208            ret.append(val[-len(decoder.buffer) * 3 :])209 210        ret2 = "".join(ret)211        if ret2 == val:212            return val213        return ret2214 
codekingpro/portable-devtools · Team Ai