tor-browser

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

converters.py (3622B)


      1 # SPDX-License-Identifier: MIT
      2 
      3 """
      4 Commonly useful converters.
      5 """
      6 
      7 
      8 import typing
      9 
     10 from ._compat import _AnnotationExtractor
     11 from ._make import NOTHING, Factory, pipe
     12 
     13 
     14 __all__ = [
     15    "default_if_none",
     16    "optional",
     17    "pipe",
     18    "to_bool",
     19 ]
     20 
     21 
     22 def optional(converter):
     23    """
     24    A converter that allows an attribute to be optional. An optional attribute
     25    is one which can be set to ``None``.
     26 
     27    Type annotations will be inferred from the wrapped converter's, if it
     28    has any.
     29 
     30    :param callable converter: the converter that is used for non-``None``
     31        values.
     32 
     33    .. versionadded:: 17.1.0
     34    """
     35 
     36    def optional_converter(val):
     37        if val is None:
     38            return None
     39        return converter(val)
     40 
     41    xtr = _AnnotationExtractor(converter)
     42 
     43    t = xtr.get_first_param_type()
     44    if t:
     45        optional_converter.__annotations__["val"] = typing.Optional[t]
     46 
     47    rt = xtr.get_return_type()
     48    if rt:
     49        optional_converter.__annotations__["return"] = typing.Optional[rt]
     50 
     51    return optional_converter
     52 
     53 
     54 def default_if_none(default=NOTHING, factory=None):
     55    """
     56    A converter that allows to replace ``None`` values by *default* or the
     57    result of *factory*.
     58 
     59    :param default: Value to be used if ``None`` is passed. Passing an instance
     60       of `attrs.Factory` is supported, however the ``takes_self`` option
     61       is *not*.
     62    :param callable factory: A callable that takes no parameters whose result
     63       is used if ``None`` is passed.
     64 
     65    :raises TypeError: If **neither** *default* or *factory* is passed.
     66    :raises TypeError: If **both** *default* and *factory* are passed.
     67    :raises ValueError: If an instance of `attrs.Factory` is passed with
     68       ``takes_self=True``.
     69 
     70    .. versionadded:: 18.2.0
     71    """
     72    if default is NOTHING and factory is None:
     73        msg = "Must pass either `default` or `factory`."
     74        raise TypeError(msg)
     75 
     76    if default is not NOTHING and factory is not None:
     77        msg = "Must pass either `default` or `factory` but not both."
     78        raise TypeError(msg)
     79 
     80    if factory is not None:
     81        default = Factory(factory)
     82 
     83    if isinstance(default, Factory):
     84        if default.takes_self:
     85            msg = "`takes_self` is not supported by default_if_none."
     86            raise ValueError(msg)
     87 
     88        def default_if_none_converter(val):
     89            if val is not None:
     90                return val
     91 
     92            return default.factory()
     93 
     94    else:
     95 
     96        def default_if_none_converter(val):
     97            if val is not None:
     98                return val
     99 
    100            return default
    101 
    102    return default_if_none_converter
    103 
    104 
    105 def to_bool(val):
    106    """
    107    Convert "boolean" strings (e.g., from env. vars.) to real booleans.
    108 
    109    Values mapping to :code:`True`:
    110 
    111    - :code:`True`
    112    - :code:`"true"` / :code:`"t"`
    113    - :code:`"yes"` / :code:`"y"`
    114    - :code:`"on"`
    115    - :code:`"1"`
    116    - :code:`1`
    117 
    118    Values mapping to :code:`False`:
    119 
    120    - :code:`False`
    121    - :code:`"false"` / :code:`"f"`
    122    - :code:`"no"` / :code:`"n"`
    123    - :code:`"off"`
    124    - :code:`"0"`
    125    - :code:`0`
    126 
    127    :raises ValueError: for any other value.
    128 
    129    .. versionadded:: 21.3.0
    130    """
    131    if isinstance(val, str):
    132        val = val.lower()
    133    truthy = {True, "true", "t", "yes", "y", "on", "1", 1}
    134    falsy = {False, "false", "f", "no", "n", "off", "0", 0}
    135    try:
    136        if val in truthy:
    137            return True
    138        if val in falsy:
    139            return False
    140    except TypeError:
    141        # Raised when "val" is not hashable (e.g., lists)
    142        pass
    143    msg = f"Cannot convert value to bool: {val}"
    144    raise ValueError(msg)