You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			163 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			163 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
# SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
"""
 | 
						|
Commonly useful converters.
 | 
						|
"""
 | 
						|
 | 
						|
import typing
 | 
						|
 | 
						|
from ._compat import _AnnotationExtractor
 | 
						|
from ._make import NOTHING, Converter, Factory, pipe
 | 
						|
 | 
						|
 | 
						|
__all__ = [
 | 
						|
    "default_if_none",
 | 
						|
    "optional",
 | 
						|
    "pipe",
 | 
						|
    "to_bool",
 | 
						|
]
 | 
						|
 | 
						|
 | 
						|
def optional(converter):
 | 
						|
    """
 | 
						|
    A converter that allows an attribute to be optional. An optional attribute
 | 
						|
    is one which can be set to `None`.
 | 
						|
 | 
						|
    Type annotations will be inferred from the wrapped converter's, if it has
 | 
						|
    any.
 | 
						|
 | 
						|
    Args:
 | 
						|
        converter (typing.Callable):
 | 
						|
            the converter that is used for non-`None` values.
 | 
						|
 | 
						|
    .. versionadded:: 17.1.0
 | 
						|
    """
 | 
						|
 | 
						|
    if isinstance(converter, Converter):
 | 
						|
 | 
						|
        def optional_converter(val, inst, field):
 | 
						|
            if val is None:
 | 
						|
                return None
 | 
						|
            return converter(val, inst, field)
 | 
						|
 | 
						|
    else:
 | 
						|
 | 
						|
        def optional_converter(val):
 | 
						|
            if val is None:
 | 
						|
                return None
 | 
						|
            return converter(val)
 | 
						|
 | 
						|
    xtr = _AnnotationExtractor(converter)
 | 
						|
 | 
						|
    t = xtr.get_first_param_type()
 | 
						|
    if t:
 | 
						|
        optional_converter.__annotations__["val"] = typing.Optional[t]
 | 
						|
 | 
						|
    rt = xtr.get_return_type()
 | 
						|
    if rt:
 | 
						|
        optional_converter.__annotations__["return"] = typing.Optional[rt]
 | 
						|
 | 
						|
    if isinstance(converter, Converter):
 | 
						|
        return Converter(optional_converter, takes_self=True, takes_field=True)
 | 
						|
 | 
						|
    return optional_converter
 | 
						|
 | 
						|
 | 
						|
def default_if_none(default=NOTHING, factory=None):
 | 
						|
    """
 | 
						|
    A converter that allows to replace `None` values by *default* or the result
 | 
						|
    of *factory*.
 | 
						|
 | 
						|
    Args:
 | 
						|
        default:
 | 
						|
            Value to be used if `None` is passed. Passing an instance of
 | 
						|
            `attrs.Factory` is supported, however the ``takes_self`` option is
 | 
						|
            *not*.
 | 
						|
 | 
						|
        factory (typing.Callable):
 | 
						|
            A callable that takes no parameters whose result is used if `None`
 | 
						|
            is passed.
 | 
						|
 | 
						|
    Raises:
 | 
						|
        TypeError: If **neither** *default* or *factory* is passed.
 | 
						|
 | 
						|
        TypeError: If **both** *default* and *factory* are passed.
 | 
						|
 | 
						|
        ValueError:
 | 
						|
            If an instance of `attrs.Factory` is passed with
 | 
						|
            ``takes_self=True``.
 | 
						|
 | 
						|
    .. versionadded:: 18.2.0
 | 
						|
    """
 | 
						|
    if default is NOTHING and factory is None:
 | 
						|
        msg = "Must pass either `default` or `factory`."
 | 
						|
        raise TypeError(msg)
 | 
						|
 | 
						|
    if default is not NOTHING and factory is not None:
 | 
						|
        msg = "Must pass either `default` or `factory` but not both."
 | 
						|
        raise TypeError(msg)
 | 
						|
 | 
						|
    if factory is not None:
 | 
						|
        default = Factory(factory)
 | 
						|
 | 
						|
    if isinstance(default, Factory):
 | 
						|
        if default.takes_self:
 | 
						|
            msg = "`takes_self` is not supported by default_if_none."
 | 
						|
            raise ValueError(msg)
 | 
						|
 | 
						|
        def default_if_none_converter(val):
 | 
						|
            if val is not None:
 | 
						|
                return val
 | 
						|
 | 
						|
            return default.factory()
 | 
						|
 | 
						|
    else:
 | 
						|
 | 
						|
        def default_if_none_converter(val):
 | 
						|
            if val is not None:
 | 
						|
                return val
 | 
						|
 | 
						|
            return default
 | 
						|
 | 
						|
    return default_if_none_converter
 | 
						|
 | 
						|
 | 
						|
def to_bool(val):
 | 
						|
    """
 | 
						|
    Convert "boolean" strings (for example, from environment variables) to real
 | 
						|
    booleans.
 | 
						|
 | 
						|
    Values mapping to `True`:
 | 
						|
 | 
						|
    - ``True``
 | 
						|
    - ``"true"`` / ``"t"``
 | 
						|
    - ``"yes"`` / ``"y"``
 | 
						|
    - ``"on"``
 | 
						|
    - ``"1"``
 | 
						|
    - ``1``
 | 
						|
 | 
						|
    Values mapping to `False`:
 | 
						|
 | 
						|
    - ``False``
 | 
						|
    - ``"false"`` / ``"f"``
 | 
						|
    - ``"no"`` / ``"n"``
 | 
						|
    - ``"off"``
 | 
						|
    - ``"0"``
 | 
						|
    - ``0``
 | 
						|
 | 
						|
    Raises:
 | 
						|
        ValueError: For any other value.
 | 
						|
 | 
						|
    .. versionadded:: 21.3.0
 | 
						|
    """
 | 
						|
    if isinstance(val, str):
 | 
						|
        val = val.lower()
 | 
						|
 | 
						|
    if val in (True, "true", "t", "yes", "y", "on", "1", 1):
 | 
						|
        return True
 | 
						|
    if val in (False, "false", "f", "no", "n", "off", "0", 0):
 | 
						|
        return False
 | 
						|
 | 
						|
    msg = f"Cannot convert value to bool: {val!r}"
 | 
						|
    raise ValueError(msg)
 |