mercurial/thirdparty/attr/converters.py
changeset 49643 e1c586b9a43c
parent 34397 765eb17a7eb8
equal deleted inserted replaced
49642:7e6f3c69c0fb 49643:e1c586b9a43c
       
     1 # SPDX-License-Identifier: MIT
       
     2 
     1 """
     3 """
     2 Commonly useful converters.
     4 Commonly useful converters.
     3 """
     5 """
     4 
     6 
     5 from __future__ import absolute_import, division, print_function
     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 ]
     6 
    20 
     7 
    21 
     8 def optional(converter):
    22 def optional(converter):
     9     """
    23     """
    10     A converter that allows an attribute to be optional. An optional attribute
    24     A converter that allows an attribute to be optional. An optional attribute
    11     is one which can be set to ``None``.
    25     is one which can be set to ``None``.
    12 
    26 
       
    27     Type annotations will be inferred from the wrapped converter's, if it
       
    28     has any.
       
    29 
    13     :param callable converter: the converter that is used for non-``None``
    30     :param callable converter: the converter that is used for non-``None``
    14         values.
    31         values.
    15 
    32 
    16     ..  versionadded:: 17.1.0
    33     .. versionadded:: 17.1.0
    17     """
    34     """
    18 
    35 
    19     def optional_converter(val):
    36     def optional_converter(val):
    20         if val is None:
    37         if val is None:
    21             return None
    38             return None
    22         return converter(val)
    39         return converter(val)
    23 
    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 
    24     return optional_converter
    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         raise TypeError("Must pass either `default` or `factory`.")
       
    74 
       
    75     if default is not NOTHING and factory is not None:
       
    76         raise TypeError(
       
    77             "Must pass either `default` or `factory` but not both."
       
    78         )
       
    79 
       
    80     if factory is not None:
       
    81         default = Factory(factory)
       
    82 
       
    83     if isinstance(default, Factory):
       
    84         if default.takes_self:
       
    85             raise ValueError(
       
    86                 "`takes_self` is not supported by default_if_none."
       
    87             )
       
    88 
       
    89         def default_if_none_converter(val):
       
    90             if val is not None:
       
    91                 return val
       
    92 
       
    93             return default.factory()
       
    94 
       
    95     else:
       
    96 
       
    97         def default_if_none_converter(val):
       
    98             if val is not None:
       
    99                 return val
       
   100 
       
   101             return default
       
   102 
       
   103     return default_if_none_converter
       
   104 
       
   105 
       
   106 def to_bool(val):
       
   107     """
       
   108     Convert "boolean" strings (e.g., from env. vars.) to real booleans.
       
   109 
       
   110     Values mapping to :code:`True`:
       
   111 
       
   112     - :code:`True`
       
   113     - :code:`"true"` / :code:`"t"`
       
   114     - :code:`"yes"` / :code:`"y"`
       
   115     - :code:`"on"`
       
   116     - :code:`"1"`
       
   117     - :code:`1`
       
   118 
       
   119     Values mapping to :code:`False`:
       
   120 
       
   121     - :code:`False`
       
   122     - :code:`"false"` / :code:`"f"`
       
   123     - :code:`"no"` / :code:`"n"`
       
   124     - :code:`"off"`
       
   125     - :code:`"0"`
       
   126     - :code:`0`
       
   127 
       
   128     :raises ValueError: for any other value.
       
   129 
       
   130     .. versionadded:: 21.3.0
       
   131     """
       
   132     if isinstance(val, str):
       
   133         val = val.lower()
       
   134     truthy = {True, "true", "t", "yes", "y", "on", "1", 1}
       
   135     falsy = {False, "false", "f", "no", "n", "off", "0", 0}
       
   136     try:
       
   137         if val in truthy:
       
   138             return True
       
   139         if val in falsy:
       
   140             return False
       
   141     except TypeError:
       
   142         # Raised when "val" is not hashable (e.g., lists)
       
   143         pass
       
   144     raise ValueError("Cannot convert value to bool: {}".format(val))