aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/attrs/attr/converters.py
blob: 3373374b567634ccf95b66f516429f6929799cad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""
Commonly useful converters.
"""

from __future__ import absolute_import, division, print_function

from ._compat import PY2
from ._make import NOTHING, Factory, pipe

 
if not PY2:
    import inspect
    import typing


__all__ = [
    "pipe",
    "optional",
    "default_if_none",
]


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.

    :param callable converter: the converter that is used for non-``None``
        values.

    .. versionadded:: 17.1.0 
    """

    def optional_converter(val):
        if val is None:
            return None
        return converter(val)

    if not PY2:
        sig = None
        try:
            sig = inspect.signature(converter)
        except (ValueError, TypeError):  # inspect failed
            pass
        if sig:
            params = list(sig.parameters.values())
            if params and params[0].annotation is not inspect.Parameter.empty:
                optional_converter.__annotations__["val"] = typing.Optional[
                    params[0].annotation
                ]
            if sig.return_annotation is not inspect.Signature.empty:
                optional_converter.__annotations__["return"] = typing.Optional[
                    sig.return_annotation
                ]

    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*. 
 
    :param default: Value to be used if ``None`` is passed. Passing an instance 
       of `attr.Factory` is supported, however the ``takes_self`` option
       is *not*. 
    :param callable factory: A callable that takes no parameters whose result
       is used if ``None`` is passed. 
 
    :raises TypeError: If **neither** *default* or *factory* is passed. 
    :raises TypeError: If **both** *default* and *factory* are passed. 
    :raises ValueError: If an instance of `attr.Factory` is passed with
       ``takes_self=True``. 
 
    .. versionadded:: 18.2.0 
    """ 
    if default is NOTHING and factory is None: 
        raise TypeError("Must pass either `default` or `factory`.") 
 
    if default is not NOTHING and factory is not None: 
        raise TypeError( 
            "Must pass either `default` or `factory` but not both." 
        ) 
 
    if factory is not None: 
        default = Factory(factory) 
 
    if isinstance(default, Factory): 
        if default.takes_self: 
            raise ValueError( 
                "`takes_self` is not supported by default_if_none." 
            ) 
 
        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