tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

schema.py (3166B)


      1 from typing import Any, Callable, cast, Dict, Sequence, Set, Type, TypeVar, Union
      2 
      3 T = TypeVar("T")
      4 
      5 def validate_dict(obj: Any, required_keys: Set[str] = set(), optional_keys: Set[str] = set()) -> None:
      6    """
      7    Validates the keys for a particular object
      8    This logic ensures:
      9    1. the obj is type dict
     10    2. That at a minimum the provided required_keys are present.
     11    Additionally, the logic checks for a set of optional_keys. With those two
     12    sets of keys, the logic will raise an error if there are extra keys in obj.
     13    :param obj: The object that will be checked.
     14    :param required_keys: Set of required keys that the obj should have.
     15    :param optional_keys: Set of optional keys that the obj should have.
     16    :return: `None` if obj does not have any extra keys.
     17    :raises ValueError: If there unexpected keys or missing required keys.
     18    """
     19    if not isinstance(obj, dict):
     20        raise ValueError(f"Object is not a dictionary. Input: {obj}")
     21    extra_keys = set(obj.keys()) - required_keys - optional_keys
     22    missing_required_keys = required_keys - set(obj.keys())
     23    if extra_keys:
     24        raise ValueError(f"Object contains invalid keys: {sorted(extra_keys)}")
     25    if missing_required_keys:
     26        raise ValueError(f"Object missing required keys: {sorted(missing_required_keys)}")
     27 
     28 
     29 class SchemaValue():
     30    """
     31    Set of helpers to convert raw input into an expected value for a given schema
     32    """
     33    @staticmethod
     34    def from_dict(x: Any) -> Dict[str, Any]:
     35        if not isinstance(x, dict):
     36            raise ValueError(f"Input value {x} is not a dict")
     37        keys = x.keys()
     38        for key in keys:
     39            if not isinstance(key, str):
     40                raise ValueError(f"Input value {x} contains key {key} that is not a string")
     41        return cast(Dict[str, Any], x)
     42 
     43 
     44    @staticmethod
     45    def from_str(x: Any) -> str:
     46        if not isinstance(x, str):
     47            raise ValueError(f"Input value {x} is not a string")
     48        return x
     49 
     50 
     51    @staticmethod
     52    def from_none(x: Any) -> None:
     53        if x is not None:
     54            raise ValueError(f"Input value {x} is not none")
     55        return x
     56 
     57 
     58    @staticmethod
     59    def from_union(fs:
     60        Sequence[Union[
     61            Callable[[Any], Sequence[T]],
     62            Callable[[Any], T],
     63        ]],
     64            x: Any) -> Any:
     65        for f in fs:
     66            try:
     67                return f(x)
     68            except Exception:
     69                pass
     70        raise ValueError(f"Input value {x} does not fit one of the expected values for the union")
     71 
     72 
     73    @staticmethod
     74    def from_list(f: Callable[[Any], T], x: Any) -> Sequence[T]:
     75        if not isinstance(x, list):
     76            raise ValueError(f"Input value {x} is not a list")
     77        return [f(y) for y in x]
     78 
     79 
     80    @staticmethod
     81    def from_class(cls: Type[T]) -> Callable[[Any], T]:
     82        def class_converter(x: Any) -> T:
     83            try:
     84                # https://github.com/python/mypy/issues/10343
     85                return cls(x)  # type: ignore [call-arg]
     86            except Exception:
     87                raise ValueError(f"Input value {x} could not be converted to {cls}")
     88        return class_converter