17db96d56Sopenharmony_ci# SPDX-License-Identifier: MIT
27db96d56Sopenharmony_ci# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
37db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement.
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ci"""Utilities for tests that are in the "burntsushi" format."""
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ciimport datetime
87db96d56Sopenharmony_cifrom typing import Any
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ci# Aliases for converting TOML compliance format [1] to BurntSushi format [2]
117db96d56Sopenharmony_ci# [1] https://github.com/toml-lang/compliance/blob/db7c3211fda30ff9ddb10292f4aeda7e2e10abc4/docs/json-encoding.md  # noqa: E501
127db96d56Sopenharmony_ci# [2] https://github.com/BurntSushi/toml-test/blob/4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993/README.md  # noqa: E501
137db96d56Sopenharmony_ci_aliases = {
147db96d56Sopenharmony_ci    "boolean": "bool",
157db96d56Sopenharmony_ci    "offset datetime": "datetime",
167db96d56Sopenharmony_ci    "local datetime": "datetime-local",
177db96d56Sopenharmony_ci    "local date": "date-local",
187db96d56Sopenharmony_ci    "local time": "time-local",
197db96d56Sopenharmony_ci}
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_cidef convert(obj):  # noqa: C901
237db96d56Sopenharmony_ci    if isinstance(obj, str):
247db96d56Sopenharmony_ci        return {"type": "string", "value": obj}
257db96d56Sopenharmony_ci    elif isinstance(obj, bool):
267db96d56Sopenharmony_ci        return {"type": "bool", "value": str(obj).lower()}
277db96d56Sopenharmony_ci    elif isinstance(obj, int):
287db96d56Sopenharmony_ci        return {"type": "integer", "value": str(obj)}
297db96d56Sopenharmony_ci    elif isinstance(obj, float):
307db96d56Sopenharmony_ci        return {"type": "float", "value": _normalize_float_str(str(obj))}
317db96d56Sopenharmony_ci    elif isinstance(obj, datetime.datetime):
327db96d56Sopenharmony_ci        val = _normalize_datetime_str(obj.isoformat())
337db96d56Sopenharmony_ci        if obj.tzinfo:
347db96d56Sopenharmony_ci            return {"type": "datetime", "value": val}
357db96d56Sopenharmony_ci        return {"type": "datetime-local", "value": val}
367db96d56Sopenharmony_ci    elif isinstance(obj, datetime.time):
377db96d56Sopenharmony_ci        return {
387db96d56Sopenharmony_ci            "type": "time-local",
397db96d56Sopenharmony_ci            "value": _normalize_localtime_str(str(obj)),
407db96d56Sopenharmony_ci        }
417db96d56Sopenharmony_ci    elif isinstance(obj, datetime.date):
427db96d56Sopenharmony_ci        return {
437db96d56Sopenharmony_ci            "type": "date-local",
447db96d56Sopenharmony_ci            "value": str(obj),
457db96d56Sopenharmony_ci        }
467db96d56Sopenharmony_ci    elif isinstance(obj, list):
477db96d56Sopenharmony_ci        return [convert(i) for i in obj]
487db96d56Sopenharmony_ci    elif isinstance(obj, dict):
497db96d56Sopenharmony_ci        return {k: convert(v) for k, v in obj.items()}
507db96d56Sopenharmony_ci    raise Exception("unsupported type")
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_cidef normalize(obj: Any) -> Any:
547db96d56Sopenharmony_ci    """Normalize test objects.
557db96d56Sopenharmony_ci
567db96d56Sopenharmony_ci    This normalizes primitive values (e.g. floats), and also converts from
577db96d56Sopenharmony_ci    TOML compliance format [1] to BurntSushi format [2].
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci    [1] https://github.com/toml-lang/compliance/blob/db7c3211fda30ff9ddb10292f4aeda7e2e10abc4/docs/json-encoding.md  # noqa: E501
607db96d56Sopenharmony_ci    [2] https://github.com/BurntSushi/toml-test/blob/4634fdf3a6ecd6aaea5f4cdcd98b2733c2694993/README.md  # noqa: E501
617db96d56Sopenharmony_ci    """
627db96d56Sopenharmony_ci    if isinstance(obj, list):
637db96d56Sopenharmony_ci        return [normalize(item) for item in obj]
647db96d56Sopenharmony_ci    if isinstance(obj, dict):
657db96d56Sopenharmony_ci        if "type" in obj and "value" in obj:
667db96d56Sopenharmony_ci            type_ = obj["type"]
677db96d56Sopenharmony_ci            norm_type = _aliases.get(type_, type_)
687db96d56Sopenharmony_ci            value = obj["value"]
697db96d56Sopenharmony_ci            if norm_type == "float":
707db96d56Sopenharmony_ci                norm_value = _normalize_float_str(value)
717db96d56Sopenharmony_ci            elif norm_type in {"datetime", "datetime-local"}:
727db96d56Sopenharmony_ci                norm_value = _normalize_datetime_str(value)
737db96d56Sopenharmony_ci            elif norm_type == "time-local":
747db96d56Sopenharmony_ci                norm_value = _normalize_localtime_str(value)
757db96d56Sopenharmony_ci            else:
767db96d56Sopenharmony_ci                norm_value = value
777db96d56Sopenharmony_ci
787db96d56Sopenharmony_ci            if norm_type == "array":
797db96d56Sopenharmony_ci                return [normalize(item) for item in value]
807db96d56Sopenharmony_ci            return {"type": norm_type, "value": norm_value}
817db96d56Sopenharmony_ci        return {k: normalize(v) for k, v in obj.items()}
827db96d56Sopenharmony_ci    raise AssertionError("Burntsushi fixtures should be dicts/lists only")
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_cidef _normalize_datetime_str(dt_str: str) -> str:
867db96d56Sopenharmony_ci    if dt_str[-1].lower() == "z":
877db96d56Sopenharmony_ci        dt_str = dt_str[:-1] + "+00:00"
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci    date = dt_str[:10]
907db96d56Sopenharmony_ci    rest = dt_str[11:]
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci    if "+" in rest:
937db96d56Sopenharmony_ci        sign = "+"
947db96d56Sopenharmony_ci    elif "-" in rest:
957db96d56Sopenharmony_ci        sign = "-"
967db96d56Sopenharmony_ci    else:
977db96d56Sopenharmony_ci        sign = ""
987db96d56Sopenharmony_ci
997db96d56Sopenharmony_ci    if sign:
1007db96d56Sopenharmony_ci        time, _, offset = rest.partition(sign)
1017db96d56Sopenharmony_ci    else:
1027db96d56Sopenharmony_ci        time = rest
1037db96d56Sopenharmony_ci        offset = ""
1047db96d56Sopenharmony_ci
1057db96d56Sopenharmony_ci    time = time.rstrip("0") if "." in time else time
1067db96d56Sopenharmony_ci    return date + "T" + time + sign + offset
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ci
1097db96d56Sopenharmony_cidef _normalize_localtime_str(lt_str: str) -> str:
1107db96d56Sopenharmony_ci    return lt_str.rstrip("0") if "." in lt_str else lt_str
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_cidef _normalize_float_str(float_str: str) -> str:
1147db96d56Sopenharmony_ci    as_float = float(float_str)
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    # Normalize "-0.0" and "+0.0"
1177db96d56Sopenharmony_ci    if as_float == 0:
1187db96d56Sopenharmony_ci        return "0"
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_ci    return str(as_float)
121