Add typed helpers and improve type hints in util/json (#88534)

* Add type hints to load_json

* Adjust ios

* Adjust nest

* Add use of load_json_array

* Add tests

* Adjust test patch

* Add test_load_json_os_error
This commit is contained in:
epenet
2023-02-22 10:09:28 +01:00
committed by GitHub
parent 906d397736
commit aa20c902db
7 changed files with 137 additions and 27 deletions

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
from collections.abc import Callable
import json
import logging
from os import PathLike
from typing import Any
import orjson
@@ -12,6 +13,7 @@ from homeassistant.exceptions import HomeAssistantError
from .file import WriteError # pylint: disable=unused-import # noqa: F401
_SENTINEL = object()
_LOGGER = logging.getLogger(__name__)
JsonValueType = (
@@ -54,8 +56,10 @@ def json_loads_object(__obj: bytes | bytearray | memoryview | str) -> JsonObject
raise ValueError(f"Expected JSON to be parsed as a dict got {type(value)}")
def load_json(filename: str, default: list | dict | None = None) -> list | dict:
"""Load JSON data from a file and return as dict or list.
def load_json(
filename: str | PathLike, default: JsonValueType = _SENTINEL # type: ignore[assignment]
) -> JsonValueType:
"""Load JSON data from a file.
Defaults to returning empty dict if file is not found.
"""
@@ -71,7 +75,45 @@ def load_json(filename: str, default: list | dict | None = None) -> list | dict:
except OSError as error:
_LOGGER.exception("JSON file reading failed: %s", filename)
raise HomeAssistantError(error) from error
return {} if default is None else default
return {} if default is _SENTINEL else default
def load_json_array(
filename: str | PathLike, default: JsonArrayType = _SENTINEL # type: ignore[assignment]
) -> JsonArrayType:
"""Load JSON data from a file and return as list.
Defaults to returning empty list if file is not found.
"""
if default is _SENTINEL:
default = []
value: JsonValueType = load_json(filename, default=default)
# Avoid isinstance overhead as we are not interested in list subclasses
if type(value) is list: # pylint: disable=unidiomatic-typecheck
return value
_LOGGER.exception(
"Expected JSON to be parsed as a list got %s in: %s", {type(value)}, filename
)
raise HomeAssistantError(f"Expected JSON to be parsed as a list got {type(value)}")
def load_json_object(
filename: str | PathLike, default: JsonObjectType = _SENTINEL # type: ignore[assignment]
) -> JsonObjectType:
"""Load JSON data from a file and return as dict.
Defaults to returning empty dict if file is not found.
"""
if default is _SENTINEL:
default = {}
value: JsonValueType = load_json(filename, default=default)
# Avoid isinstance overhead as we are not interested in dict subclasses
if type(value) is dict: # pylint: disable=unidiomatic-typecheck
return value
_LOGGER.exception(
"Expected JSON to be parsed as a dict got %s in: %s", {type(value)}, filename
)
raise HomeAssistantError(f"Expected JSON to be parsed as a dict got {type(value)}")
def save_json(