Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_parser.py754 linesDownload Raw Back to tomllib
1# SPDX-License-Identifier: MIT2# SPDX-FileCopyrightText: 2021 Taneli Hukkinen3# Licensed to PSF under a Contributor Agreement.4 5from __future__ import annotations6 7from types import MappingProxyType8 9from ._re import (10    RE_DATETIME,11    RE_LOCALTIME,12    RE_NUMBER,13    match_to_datetime,14    match_to_localtime,15    match_to_number,16)17 18TYPE_CHECKING = False19if TYPE_CHECKING:20    from collections.abc import Iterable21    from typing import IO, Any22 23    from ._types import Key, ParseFloat, Pos24 25ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))26 27# Neither of these sets include quotation mark or backslash. They are28# currently handled as separate cases in the parser functions.29ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t")30ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n")31 32ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS33ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ILLEGAL_MULTILINE_BASIC_STR_CHARS34 35ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS36 37TOML_WS = frozenset(" \t")38TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n")39BARE_KEY_CHARS = frozenset(40    "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_"41)42KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'")43HEXDIGIT_CHARS = frozenset("abcdef" "ABCDEF" "0123456789")44 45BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType(46    {47        "\\b": "\u0008",  # backspace48        "\\t": "\u0009",  # tab49        "\\n": "\u000A",  # linefeed50        "\\f": "\u000C",  # form feed51        "\\r": "\u000D",  # carriage return52        '\\"': "\u0022",  # quote53        "\\\\": "\u005C",  # backslash54    }55)56 57 58class DEPRECATED_DEFAULT:59    """Sentinel to be used as default arg during deprecation60    period of TOMLDecodeError's free-form arguments."""61 62 63class TOMLDecodeError(ValueError):64    """An error raised if a document is not valid TOML.65 66    Adds the following attributes to ValueError:67    msg: The unformatted error message68    doc: The TOML document being parsed69    pos: The index of doc where parsing failed70    lineno: The line corresponding to pos71    colno: The column corresponding to pos72    """73 74    def __init__(75        self,76        msg: str = DEPRECATED_DEFAULT,  # type: ignore[assignment]77        doc: str = DEPRECATED_DEFAULT,  # type: ignore[assignment]78        pos: Pos = DEPRECATED_DEFAULT,  # type: ignore[assignment]79        *args: Any,80    ):81        if (82            args83            or not isinstance(msg, str)84            or not isinstance(doc, str)85            or not isinstance(pos, int)86        ):87            import warnings88 89            warnings.warn(90                "Free-form arguments for TOMLDecodeError are deprecated. "91                "Please set 'msg' (str), 'doc' (str) and 'pos' (int) arguments only.",92                DeprecationWarning,93                stacklevel=2,94            )95            if pos is not DEPRECATED_DEFAULT:  # type: ignore[comparison-overlap]96                args = pos, *args97            if doc is not DEPRECATED_DEFAULT:  # type: ignore[comparison-overlap]98                args = doc, *args99            if msg is not DEPRECATED_DEFAULT:  # type: ignore[comparison-overlap]100                args = msg, *args101            ValueError.__init__(self, *args)102            return103 104        lineno = doc.count("\n", 0, pos) + 1105        if lineno == 1:106            colno = pos + 1107        else:108            colno = pos - doc.rindex("\n", 0, pos)109 110        if pos >= len(doc):111            coord_repr = "end of document"112        else:113            coord_repr = f"line {lineno}, column {colno}"114        errmsg = f"{msg} (at {coord_repr})"115        ValueError.__init__(self, errmsg)116 117        self.msg = msg118        self.doc = doc119        self.pos = pos120        self.lineno = lineno121        self.colno = colno122 123 124def load(fp: IO[bytes], /, *, parse_float: ParseFloat = float) -> dict[str, Any]:125    """Parse TOML from a binary file object."""126    b = fp.read()127    try:128        s = b.decode()129    except AttributeError:130        raise TypeError(131            "File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`"132        ) from None133    return loads(s, parse_float=parse_float)134 135 136def loads(s: str, /, *, parse_float: ParseFloat = float) -> dict[str, Any]:  # noqa: C901137    """Parse TOML from a string."""138 139    # The spec allows converting "\r\n" to "\n", even in string140    # literals. Let's do so to simplify parsing.141    try:142        src = s.replace("\r\n", "\n")143    except (AttributeError, TypeError):144        raise TypeError(145            f"Expected str object, not '{type(s).__qualname__}'"146        ) from None147    pos = 0148    out = Output()149    header: Key = ()150    parse_float = make_safe_parse_float(parse_float)151 152    # Parse one statement at a time153    # (typically means one line in TOML source)154    while True:155        # 1. Skip line leading whitespace156        pos = skip_chars(src, pos, TOML_WS)157 158        # 2. Parse rules. Expect one of the following:159        #    - end of file160        #    - end of line161        #    - comment162        #    - key/value pair163        #    - append dict to list (and move to its namespace)164        #    - create dict (and move to its namespace)165        # Skip trailing whitespace when applicable.166        try:167            char = src[pos]168        except IndexError:169            break170        if char == "\n":171            pos += 1172            continue173        if char in KEY_INITIAL_CHARS:174            pos = key_value_rule(src, pos, out, header, parse_float)175            pos = skip_chars(src, pos, TOML_WS)176        elif char == "[":177            try:178                second_char: str | None = src[pos + 1]179            except IndexError:180                second_char = None181            out.flags.finalize_pending()182            if second_char == "[":183                pos, header = create_list_rule(src, pos, out)184            else:185                pos, header = create_dict_rule(src, pos, out)186            pos = skip_chars(src, pos, TOML_WS)187        elif char != "#":188            raise TOMLDecodeError("Invalid statement", src, pos)189 190        # 3. Skip comment191        pos = skip_comment(src, pos)192 193        # 4. Expect end of line or end of file194        try:195            char = src[pos]196        except IndexError:197            break198        if char != "\n":199            raise TOMLDecodeError(200                "Expected newline or end of document after a statement", src, pos201            )202        pos += 1203 204    return out.data.dict205 206 207class Flags:208    """Flags that map to parsed keys/namespaces."""209 210    # Marks an immutable namespace (inline array or inline table).211    FROZEN = 0212    # Marks a nest that has been explicitly created and can no longer213    # be opened using the "[table]" syntax.214    EXPLICIT_NEST = 1215 216    def __init__(self) -> None:217        self._flags: dict[str, dict[Any, Any]] = {}218        self._pending_flags: set[tuple[Key, int]] = set()219 220    def add_pending(self, key: Key, flag: int) -> None:221        self._pending_flags.add((key, flag))222 223    def finalize_pending(self) -> None:224        for key, flag in self._pending_flags:225            self.set(key, flag, recursive=False)226        self._pending_flags.clear()227 228    def unset_all(self, key: Key) -> None:229        cont = self._flags230        for k in key[:-1]:231            if k not in cont:232                return233            cont = cont[k]["nested"]234        cont.pop(key[-1], None)235 236    def set(self, key: Key, flag: int, *, recursive: bool) -> None:  # noqa: A003237        cont = self._flags238        key_parent, key_stem = key[:-1], key[-1]239        for k in key_parent:240            if k not in cont:241                cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}}242            cont = cont[k]["nested"]243        if key_stem not in cont:244            cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}}245        cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag)246 247    def is_(self, key: Key, flag: int) -> bool:248        if not key:249            return False  # document root has no flags250        cont = self._flags251        for k in key[:-1]:252            if k not in cont:253                return False254            inner_cont = cont[k]255            if flag in inner_cont["recursive_flags"]:256                return True257            cont = inner_cont["nested"]258        key_stem = key[-1]259        if key_stem in cont:260            cont = cont[key_stem]261            return flag in cont["flags"] or flag in cont["recursive_flags"]262        return False263 264 265class NestedDict:266    def __init__(self) -> None:267        # The parsed content of the TOML document268        self.dict: dict[str, Any] = {}269 270    def get_or_create_nest(271        self,272        key: Key,273        *,274        access_lists: bool = True,275    ) -> dict[str, Any]:276        cont: Any = self.dict277        for k in key:278            if k not in cont:279                cont[k] = {}280            cont = cont[k]281            if access_lists and isinstance(cont, list):282                cont = cont[-1]283            if not isinstance(cont, dict):284                raise KeyError("There is no nest behind this key")285        return cont  # type: ignore[no-any-return]286 287    def append_nest_to_list(self, key: Key) -> None:288        cont = self.get_or_create_nest(key[:-1])289        last_key = key[-1]290        if last_key in cont:291            list_ = cont[last_key]292            if not isinstance(list_, list):293                raise KeyError("An object other than list found behind this key")294            list_.append({})295        else:296            cont[last_key] = [{}]297 298 299class Output:300    def __init__(self) -> None:301        self.data = NestedDict()302        self.flags = Flags()303 304 305def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos:306    try:307        while src[pos] in chars:308            pos += 1309    except IndexError:310        pass311    return pos312 313 314def skip_until(315    src: str,316    pos: Pos,317    expect: str,318    *,319    error_on: frozenset[str],320    error_on_eof: bool,321) -> Pos:322    try:323        new_pos = src.index(expect, pos)324    except ValueError:325        new_pos = len(src)326        if error_on_eof:327            raise TOMLDecodeError(f"Expected {expect!r}", src, new_pos) from None328 329    if not error_on.isdisjoint(src[pos:new_pos]):330        while src[pos] not in error_on:331            pos += 1332        raise TOMLDecodeError(f"Found invalid character {src[pos]!r}", src, pos)333    return new_pos334 335 336def skip_comment(src: str, pos: Pos) -> Pos:337    try:338        char: str | None = src[pos]339    except IndexError:340        char = None341    if char == "#":342        return skip_until(343            src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False344        )345    return pos346 347 348def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos:349    while True:350        pos_before_skip = pos351        pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)352        pos = skip_comment(src, pos)353        if pos == pos_before_skip:354            return pos355 356 357def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:358    pos += 1  # Skip "["359    pos = skip_chars(src, pos, TOML_WS)360    pos, key = parse_key(src, pos)361 362    if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):363        raise TOMLDecodeError(f"Cannot declare {key} twice", src, pos)364    out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)365    try:366        out.data.get_or_create_nest(key)367    except KeyError:368        raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None369 370    if not src.startswith("]", pos):371        raise TOMLDecodeError(372            "Expected ']' at the end of a table declaration", src, pos373        )374    return pos + 1, key375 376 377def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:378    pos += 2  # Skip "[["379    pos = skip_chars(src, pos, TOML_WS)380    pos, key = parse_key(src, pos)381 382    if out.flags.is_(key, Flags.FROZEN):383        raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos)384    # Free the namespace now that it points to another empty list item...385    out.flags.unset_all(key)386    # ...but this key precisely is still prohibited from table declaration387    out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)388    try:389        out.data.append_nest_to_list(key)390    except KeyError:391        raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None392 393    if not src.startswith("]]", pos):394        raise TOMLDecodeError(395            "Expected ']]' at the end of an array declaration", src, pos396        )397    return pos + 2, key398 399 400def key_value_rule(401    src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat402) -> Pos:403    pos, key, value = parse_key_value_pair(src, pos, parse_float)404    key_parent, key_stem = key[:-1], key[-1]405    abs_key_parent = header + key_parent406 407    relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))408    for cont_key in relative_path_cont_keys:409        # Check that dotted key syntax does not redefine an existing table410        if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):411            raise TOMLDecodeError(f"Cannot redefine namespace {cont_key}", src, pos)412        # Containers in the relative path can't be opened with the table syntax or413        # dotted key/value syntax in following table sections.414        out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)415 416    if out.flags.is_(abs_key_parent, Flags.FROZEN):417        raise TOMLDecodeError(418            f"Cannot mutate immutable namespace {abs_key_parent}", src, pos419        )420 421    try:422        nest = out.data.get_or_create_nest(abs_key_parent)423    except KeyError:424        raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None425    if key_stem in nest:426        raise TOMLDecodeError("Cannot overwrite a value", src, pos)427    # Mark inline table and array namespaces recursively immutable428    if isinstance(value, (dict, list)):429        out.flags.set(header + key, Flags.FROZEN, recursive=True)430    nest[key_stem] = value431    return pos432 433 434def parse_key_value_pair(435    src: str, pos: Pos, parse_float: ParseFloat436) -> tuple[Pos, Key, Any]:437    pos, key = parse_key(src, pos)438    try:439        char: str | None = src[pos]440    except IndexError:441        char = None442    if char != "=":443        raise TOMLDecodeError("Expected '=' after a key in a key/value pair", src, pos)444    pos += 1445    pos = skip_chars(src, pos, TOML_WS)446    pos, value = parse_value(src, pos, parse_float)447    return pos, key, value448 449 450def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:451    pos, key_part = parse_key_part(src, pos)452    key: Key = (key_part,)453    pos = skip_chars(src, pos, TOML_WS)454    while True:455        try:456            char: str | None = src[pos]457        except IndexError:458            char = None459        if char != ".":460            return pos, key461        pos += 1462        pos = skip_chars(src, pos, TOML_WS)463        pos, key_part = parse_key_part(src, pos)464        key += (key_part,)465        pos = skip_chars(src, pos, TOML_WS)466 467 468def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]:469    try:470        char: str | None = src[pos]471    except IndexError:472        char = None473    if char in BARE_KEY_CHARS:474        start_pos = pos475        pos = skip_chars(src, pos, BARE_KEY_CHARS)476        return pos, src[start_pos:pos]477    if char == "'":478        return parse_literal_str(src, pos)479    if char == '"':480        return parse_one_line_basic_str(src, pos)481    raise TOMLDecodeError("Invalid initial character for a key part", src, pos)482 483 484def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]:485    pos += 1486    return parse_basic_str(src, pos, multiline=False)487 488 489def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list[Any]]:490    pos += 1491    array: list[Any] = []492 493    pos = skip_comments_and_array_ws(src, pos)494    if src.startswith("]", pos):495        return pos + 1, array496    while True:497        pos, val = parse_value(src, pos, parse_float)498        array.append(val)499        pos = skip_comments_and_array_ws(src, pos)500 501        c = src[pos : pos + 1]502        if c == "]":503            return pos + 1, array504        if c != ",":505            raise TOMLDecodeError("Unclosed array", src, pos)506        pos += 1507 508        pos = skip_comments_and_array_ws(src, pos)509        if src.startswith("]", pos):510            return pos + 1, array511 512 513def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict[str, Any]]:514    pos += 1515    nested_dict = NestedDict()516    flags = Flags()517 518    pos = skip_chars(src, pos, TOML_WS)519    if src.startswith("}", pos):520        return pos + 1, nested_dict.dict521    while True:522        pos, key, value = parse_key_value_pair(src, pos, parse_float)523        key_parent, key_stem = key[:-1], key[-1]524        if flags.is_(key, Flags.FROZEN):525            raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos)526        try:527            nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)528        except KeyError:529            raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None530        if key_stem in nest:531            raise TOMLDecodeError(f"Duplicate inline table key {key_stem!r}", src, pos)532        nest[key_stem] = value533        pos = skip_chars(src, pos, TOML_WS)534        c = src[pos : pos + 1]535        if c == "}":536            return pos + 1, nested_dict.dict537        if c != ",":538            raise TOMLDecodeError("Unclosed inline table", src, pos)539        if isinstance(value, (dict, list)):540            flags.set(key, Flags.FROZEN, recursive=True)541        pos += 1542        pos = skip_chars(src, pos, TOML_WS)543 544 545def parse_basic_str_escape(546    src: str, pos: Pos, *, multiline: bool = False547) -> tuple[Pos, str]:548    escape_id = src[pos : pos + 2]549    pos += 2550    if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}:551        # Skip whitespace until next non-whitespace character or end of552        # the doc. Error if non-whitespace is found before newline.553        if escape_id != "\\\n":554            pos = skip_chars(src, pos, TOML_WS)555            try:556                char = src[pos]557            except IndexError:558                return pos, ""559            if char != "\n":560                raise TOMLDecodeError("Unescaped '\\' in a string", src, pos)561            pos += 1562        pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)563        return pos, ""564    if escape_id == "\\u":565        return parse_hex_char(src, pos, 4)566    if escape_id == "\\U":567        return parse_hex_char(src, pos, 8)568    try:569        return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]570    except KeyError:571        raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) from None572 573 574def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]:575    return parse_basic_str_escape(src, pos, multiline=True)576 577 578def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]:579    hex_str = src[pos : pos + hex_len]580    if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):581        raise TOMLDecodeError("Invalid hex value", src, pos)582    pos += hex_len583    hex_int = int(hex_str, 16)584    if not is_unicode_scalar_value(hex_int):585        raise TOMLDecodeError(586            "Escaped character is not a Unicode scalar value", src, pos587        )588    return pos, chr(hex_int)589 590 591def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]:592    pos += 1  # Skip starting apostrophe593    start_pos = pos594    pos = skip_until(595        src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True596    )597    return pos + 1, src[start_pos:pos]  # Skip ending apostrophe598 599 600def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]:601    pos += 3602    if src.startswith("\n", pos):603        pos += 1604 605    if literal:606        delim = "'"607        end_pos = skip_until(608            src,609            pos,610            "'''",611            error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,612            error_on_eof=True,613        )614        result = src[pos:end_pos]615        pos = end_pos + 3616    else:617        delim = '"'618        pos, result = parse_basic_str(src, pos, multiline=True)619 620    # Add at maximum two extra apostrophes/quotes if the end sequence621    # is 4 or 5 chars long instead of just 3.622    if not src.startswith(delim, pos):623        return pos, result624    pos += 1625    if not src.startswith(delim, pos):626        return pos, result + delim627    pos += 1628    return pos, result + (delim * 2)629 630 631def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]:632    if multiline:633        error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS634        parse_escapes = parse_basic_str_escape_multiline635    else:636        error_on = ILLEGAL_BASIC_STR_CHARS637        parse_escapes = parse_basic_str_escape638    result = ""639    start_pos = pos640    while True:641        try:642            char = src[pos]643        except IndexError:644            raise TOMLDecodeError("Unterminated string", src, pos) from None645        if char == '"':646            if not multiline:647                return pos + 1, result + src[start_pos:pos]648            if src.startswith('"""', pos):649                return pos + 3, result + src[start_pos:pos]650            pos += 1651            continue652        if char == "\\":653            result += src[start_pos:pos]654            pos, parsed_escape = parse_escapes(src, pos)655            result += parsed_escape656            start_pos = pos657            continue658        if char in error_on:659            raise TOMLDecodeError(f"Illegal character {char!r}", src, pos)660        pos += 1661 662 663def parse_value(  # noqa: C901664    src: str, pos: Pos, parse_float: ParseFloat665) -> tuple[Pos, Any]:666    try:667        char: str | None = src[pos]668    except IndexError:669        char = None670 671    # IMPORTANT: order conditions based on speed of checking and likelihood672 673    # Basic strings674    if char == '"':675        if src.startswith('"""', pos):676            return parse_multiline_str(src, pos, literal=False)677        return parse_one_line_basic_str(src, pos)678 679    # Literal strings680    if char == "'":681        if src.startswith("'''", pos):682            return parse_multiline_str(src, pos, literal=True)683        return parse_literal_str(src, pos)684 685    # Booleans686    if char == "t":687        if src.startswith("true", pos):688            return pos + 4, True689    if char == "f":690        if src.startswith("false", pos):691            return pos + 5, False692 693    # Arrays694    if char == "[":695        return parse_array(src, pos, parse_float)696 697    # Inline tables698    if char == "{":699        return parse_inline_table(src, pos, parse_float)700 701    # Dates and times702    datetime_match = RE_DATETIME.match(src, pos)703    if datetime_match:704        try:705            datetime_obj = match_to_datetime(datetime_match)706        except ValueError as e:707            raise TOMLDecodeError("Invalid date or datetime", src, pos) from e708        return datetime_match.end(), datetime_obj709    localtime_match = RE_LOCALTIME.match(src, pos)710    if localtime_match:711        return localtime_match.end(), match_to_localtime(localtime_match)712 713    # Integers and "normal" floats.714    # The regex will greedily match any type starting with a decimal715    # char, so needs to be located after handling of dates and times.716    number_match = RE_NUMBER.match(src, pos)717    if number_match:718        return number_match.end(), match_to_number(number_match, parse_float)719 720    # Special floats721    first_three = src[pos : pos + 3]722    if first_three in {"inf", "nan"}:723        return pos + 3, parse_float(first_three)724    first_four = src[pos : pos + 4]725    if first_four in {"-inf", "+inf", "-nan", "+nan"}:726        return pos + 4, parse_float(first_four)727 728    raise TOMLDecodeError("Invalid value", src, pos)729 730 731def is_unicode_scalar_value(codepoint: int) -> bool:732    return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)733 734 735def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat:736    """A decorator to make `parse_float` safe.737 738    `parse_float` must not return dicts or lists, because these types739    would be mixed with parsed TOML tables and arrays, thus confusing740    the parser. The returned decorated callable raises `ValueError`741    instead of returning illegal types.742    """743    # The default `float` callable never returns illegal types. Optimize it.744    if parse_float is float:745        return float746 747    def safe_parse_float(float_str: str) -> Any:748        float_value = parse_float(float_str)749        if isinstance(float_value, (dict, list)):750            raise ValueError("parse_float must not return dicts or lists")751        return float_value752 753    return safe_parse_float754 
codekingpro/portable-devtools · Team Ai