Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/attr/converters.py: 26%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

54 statements  

1# SPDX-License-Identifier: MIT 

2 

3""" 

4Commonly useful converters. 

5""" 

6 

7 

8import typing 

9 

10from ._compat import _AnnotationExtractor 

11from ._make import NOTHING, Factory, pipe 

12 

13 

14__all__ = [ 

15 "default_if_none", 

16 "optional", 

17 "pipe", 

18 "to_bool", 

19] 

20 

21 

22def 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 

54def 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 

105def 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)