Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/invoke/parser/argument.py: 23%

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

73 statements  

1from typing import Any, Iterable, Optional, Tuple 

2 

3# TODO: dynamic type for kind 

4# T = TypeVar('T') 

5 

6 

7class Argument: 

8 """ 

9 A command-line argument/flag. 

10 

11 :param name: 

12 Syntactic sugar for ``names=[<name>]``. Giving both ``name`` and 

13 ``names`` is invalid. 

14 :param names: 

15 List of valid identifiers for this argument. For example, a "help" 

16 argument may be defined with a name list of ``['-h', '--help']``. 

17 :param kind: 

18 Type factory & parser hint. E.g. ``int`` will turn the default text 

19 value parsed, into a Python integer; and ``bool`` will tell the 

20 parser not to expect an actual value but to treat the argument as a 

21 toggle/flag. 

22 :param default: 

23 Default value made available to the parser if no value is given on the 

24 command line. 

25 :param help: 

26 Help text, intended for use with ``--help``. 

27 :param positional: 

28 Whether or not this argument's value may be given positionally. When 

29 ``False`` (default) arguments must be explicitly named. 

30 :param optional: 

31 Whether or not this (non-``bool``) argument requires a value. 

32 :param incrementable: 

33 Whether or not this (``int``) argument is to be incremented instead of 

34 overwritten/assigned to. 

35 :param attr_name: 

36 A Python identifier/attribute friendly name, typically filled in with 

37 the underscored version when ``name``/``names`` contain dashes. 

38 

39 .. versionadded:: 1.0 

40 """ 

41 

42 def __init__( 

43 self, 

44 name: Optional[str] = None, 

45 names: Iterable[str] = (), 

46 kind: Any = str, 

47 default: Optional[Any] = None, 

48 help: Optional[str] = None, 

49 positional: bool = False, 

50 optional: bool = False, 

51 incrementable: bool = False, 

52 attr_name: Optional[str] = None, 

53 ) -> None: 

54 if name and names: 

55 raise TypeError( 

56 "Cannot give both 'name' and 'names' arguments! Pick one." 

57 ) 

58 if not (name or names): 

59 raise TypeError("An Argument must have at least one name.") 

60 if names: 

61 self.names = tuple(names) 

62 elif name and not names: 

63 self.names = (name,) 

64 self.kind = kind 

65 initial_value: Optional[Any] = None 

66 # Special case: list-type args start out as empty list, not None. 

67 if kind is list: 

68 initial_value = [] 

69 # Another: incrementable args start out as their default value. 

70 if incrementable: 

71 initial_value = default 

72 self.raw_value = self._value = initial_value 

73 self.default = default 

74 self.help = help 

75 self.positional = positional 

76 self.optional = optional 

77 self.incrementable = incrementable 

78 self.attr_name = attr_name 

79 

80 def __repr__(self) -> str: 

81 nicks = "" 

82 if self.nicknames: 

83 nicks = " ({})".format(", ".join(self.nicknames)) 

84 flags = "" 

85 if self.positional or self.optional: 

86 flags = " " 

87 if self.positional: 

88 flags += "*" 

89 if self.optional: 

90 flags += "?" 

91 # TODO: store this default value somewhere other than signature of 

92 # Argument.__init__? 

93 kind = "" 

94 if self.kind != str: 

95 kind = " [{}]".format(self.kind.__name__) 

96 return "<{}: {}{}{}{}>".format( 

97 self.__class__.__name__, self.name, nicks, kind, flags 

98 ) 

99 

100 @property 

101 def name(self) -> Optional[str]: 

102 """ 

103 The canonical attribute-friendly name for this argument. 

104 

105 Will be ``attr_name`` (if given to constructor) or the first name in 

106 ``names`` otherwise. 

107 

108 .. versionadded:: 1.0 

109 """ 

110 return self.attr_name or self.names[0] 

111 

112 @property 

113 def nicknames(self) -> Tuple[str, ...]: 

114 return self.names[1:] 

115 

116 @property 

117 def takes_value(self) -> bool: 

118 if self.kind is bool: 

119 return False 

120 if self.incrementable: 

121 return False 

122 return True 

123 

124 @property 

125 def value(self) -> Any: 

126 # TODO: should probably be optional instead 

127 return self._value if self._value is not None else self.default 

128 

129 @value.setter 

130 def value(self, arg: str) -> None: 

131 self.set_value(arg, cast=True) 

132 

133 def set_value(self, value: Any, cast: bool = True) -> None: 

134 """ 

135 Actual explicit value-setting API call. 

136 

137 Sets ``self.raw_value`` to ``value`` directly. 

138 

139 Sets ``self.value`` to ``self.kind(value)``, unless: 

140 

141 - ``cast=False``, in which case the raw value is also used. 

142 - ``self.kind==list``, in which case the value is appended to 

143 ``self.value`` instead of cast & overwritten. 

144 - ``self.incrementable==True``, in which case the value is ignored and 

145 the current (assumed int) value is simply incremented. 

146 

147 .. versionadded:: 1.0 

148 """ 

149 self.raw_value = value 

150 # Default to do-nothing/identity function 

151 func = lambda x: x 

152 # If cast, set to self.kind, which should be str/int/etc 

153 if cast: 

154 func = self.kind 

155 # If self.kind is a list, append instead of using cast func. 

156 if self.kind is list: 

157 func = lambda x: self.value + [x] 

158 # If incrementable, just increment. 

159 if self.incrementable: 

160 # TODO: explode nicely if self.value was not an int to start 

161 # with 

162 func = lambda x: self.value + 1 

163 self._value = func(value) 

164 

165 @property 

166 def got_value(self) -> bool: 

167 """ 

168 Returns whether the argument was ever given a (non-default) value. 

169 

170 For most argument kinds, this simply checks whether the internally 

171 stored value is non-``None``; for others, such as ``list`` kinds, 

172 different checks may be used. 

173 

174 .. versionadded:: 1.3 

175 """ 

176 if self.kind is list: 

177 return bool(self._value) 

178 return self._value is not None