Coverage for pyTooling / CLIAbstraction / ValuedFlagList.py: 97%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-08 23:46 +0000

1# ==================================================================================================================== # 

2# _____ _ _ ____ _ ___ _ _ _ _ _ # 

3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___| | |_ _| / \ | |__ ___| |_ _ __ __ _ ___| |_(_) ___ _ __ # 

4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | | | | | / _ \ | '_ \/ __| __| '__/ _` |/ __| __| |/ _ \| '_ \ # 

5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |___| |___ | | / ___ \| |_) \__ \ |_| | | (_| | (__| |_| | (_) | | | | # 

6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____|_____|___/_/ \_\_.__/|___/\__|_| \__,_|\___|\__|_|\___/|_| |_| # 

7# |_| |___/ |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2017-2026 Patrick Lehmann - Bötzingen, Germany # 

15# Copyright 2014-2016 Technische Universität Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture # 

16# # 

17# Licensed under the Apache License, Version 2.0 (the "License"); # 

18# you may not use this file except in compliance with the License. # 

19# You may obtain a copy of the License at # 

20# # 

21# http://www.apache.org/licenses/LICENSE-2.0 # 

22# # 

23# Unless required by applicable law or agreed to in writing, software # 

24# distributed under the License is distributed on an "AS IS" BASIS, # 

25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

26# See the License for the specific language governing permissions and # 

27# limitations under the License. # 

28# # 

29# SPDX-License-Identifier: Apache-2.0 # 

30# ==================================================================================================================== # 

31# 

32""" 

33List of valued flags are argument lists where each item is a valued flag (See 

34:mod:`~pyTooling.CLIAbstraction.ValuedFlag.ValuedFlag`). 

35 

36Each list item gets translated into a ``***ValuedFlag``, with the same flag name, but differing values. 

37 

38.. seealso:: 

39 

40 * For single valued flags. |br| 

41 |rarr| :mod:`~pyTooling.CLIAbstraction.ValuedFlag` 

42 * For list of strings. |br| 

43 |rarr| :mod:`~pyTooling.CLIAbstraction.Argument.StringListArgument` 

44 * For list of paths. |br| 

45 |rarr| :mod:`~pyTooling.CLIAbstraction.Argument.PathListArgument` 

46""" 

47from typing import List, Union, Iterable, cast, Any, Self 

48 

49try: 

50 from pyTooling.Decorators import export 

51 from pyTooling.Common import getFullyQualifiedName 

52 from pyTooling.CLIAbstraction.Argument import ValueT, NamedAndValuedArgument 

53except (ImportError, ModuleNotFoundError): # pragma: no cover 

54 print("[pyTooling.Versioning] Could not import from 'pyTooling.*'!") 

55 

56 try: 

57 from Decorators import export 

58 from Common import getFullyQualifiedName 

59 from CLIAbstraction.Argument import ValueT, NamedAndValuedArgument 

60 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover 

61 print("[pyTooling.Versioning] Could not import directly!") 

62 raise ex 

63 

64 

65@export 

66class ValuedFlagList(NamedAndValuedArgument, pattern="{0}={1}"): 

67 """ 

68 Class and base-class for all ValuedFlagList classes, which represents a list of valued flags. 

69 

70 Each list element gets translated to a valued flag using the pattern for formatting. 

71 See :mod:`~pyTooling.CLIAbstraction.ValuedFlag` for more details on valued flags. 

72 

73 **Example:** 

74 

75 * ``file=file1.log file=file2.log`` 

76 """ 

77 

78 def __init_subclass__(cls, *args: Any, pattern: str = "{0}={1}", **kwargs: Any) -> None: 

79 """ 

80 This method is called when a class is derived. 

81 

82 :param args: Any positional arguments. 

83 :param pattern: This pattern is used to format an argument. |br| 

84 Default: ``"{0}={1}"``. 

85 :param kwargs: Any keyword argument. 

86 """ 

87 kwargs["pattern"] = pattern 

88 super().__init_subclass__(*args, **kwargs) 

89 

90 # TODO: the whole class should be marked as abstract 

91 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code 

92 def __new__(cls, *args: Any, **kwargs: Any) -> Self: 

93 """ 

94 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error. 

95 

96 :param args: Any positional arguments. 

97 :param kwargs: Any keyword arguments. 

98 :raises TypeError: When this class gets directly instantiated without being derived to a subclass. 

99 """ 

100 if cls is ValuedFlagList: 

101 raise TypeError(f"Class '{cls.__name__}' is abstract.") 

102 return super().__new__(cls, *args, **kwargs) 

103 

104 def __init__(self, value: List[ValueT]) -> None: 

105 super().__init__(list(value)) 

106 

107 @property 

108 def Value(self) -> List[str]: 

109 """ 

110 Get the internal value. 

111 

112 :return: Internal value. 

113 """ 

114 return self._value 

115 

116 @Value.setter 

117 def Value(self, values: Iterable[str]) -> None: 

118 """ 

119 Set the internal value. 

120 

121 :param values: List of values to set. 

122 :raises ValueError: If a list element is not o type :class:`str`. 

123 """ 

124 innerList = cast(list, self._value) 

125 innerList.clear() 

126 for value in values: 

127 if not isinstance(value, str): 

128 ex = TypeError(f"Value contains elements which are not of type 'str'.") 

129 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

130 raise ex 

131 innerList.append(value) 

132 

133 def AsArgument(self) -> Union[str, Iterable[str]]: 

134 if self._name is None: 134 ↛ 135line 134 didn't jump to line 135 because the condition on line 134 was never true

135 raise ValueError(f"") # XXX: add message 

136 

137 return [self._pattern.format(self._name, value) for value in self._value] 

138 

139 def __str__(self) -> str: 

140 """ 

141 Return a string representation of this argument instance. 

142 

143 :return: Space separated sequence of arguments formatted and each enclosed in double quotes. 

144 """ 

145 return " ".join([f"\"{value}\"" for value in self.AsArgument()]) 

146 

147 def __repr__(self) -> str: 

148 """ 

149 Return a string representation of this argument instance. 

150 

151 :return: Comma separated sequence of arguments formatted and each enclosed in double quotes. 

152 """ 

153 return ", ".join([f"\"{value}\"" for value in self.AsArgument()]) 

154 

155 

156@export 

157class ShortValuedFlagList(ValuedFlagList, pattern="-{0}={1}"): 

158 """ 

159 Represents a :py:class:`ValuedFlagArgument` with a single dash. 

160 

161 **Example:** 

162 

163 * ``-file=file1.log -file=file2.log`` 

164 """ 

165 

166 def __init_subclass__(cls, *args: Any, pattern: str = "-{0}={1}", **kwargs: Any) -> None: 

167 """ 

168 This method is called when a class is derived. 

169 

170 :param args: Any positional arguments. 

171 :param pattern: This pattern is used to format an argument. |br| 

172 Default: ``"-{0}={1}"``. 

173 :param kwargs: Any keyword argument. 

174 """ 

175 kwargs["pattern"] = pattern 

176 super().__init_subclass__(*args, **kwargs) 

177 

178 # TODO: the whole class should be marked as abstract 

179 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code 

180 def __new__(cls, *args: Any, **kwargs: Any) -> Self: 

181 """ 

182 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error. 

183 

184 :param args: Any positional arguments. 

185 :param kwargs: Any keyword arguments. 

186 :raises TypeError: When this class gets directly instantiated without being derived to a subclass. 

187 """ 

188 if cls is ShortValuedFlagList: 

189 raise TypeError(f"Class '{cls.__name__}' is abstract.") 

190 return super().__new__(cls, *args, **kwargs) 

191 

192 

193@export 

194class LongValuedFlagList(ValuedFlagList, pattern="--{0}={1}"): 

195 """ 

196 Represents a :py:class:`ValuedFlagArgument` with a double dash. 

197 

198 **Example:** 

199 

200 * ``--file=file1.log --file=file2.log`` 

201 """ 

202 

203 def __init_subclass__(cls, *args: Any, pattern: str = "--{0}={1}", **kwargs: Any) -> None: 

204 """ 

205 This method is called when a class is derived. 

206 

207 :param args: Any positional arguments. 

208 :param pattern: This pattern is used to format an argument. |br| 

209 Default: ``"--{0}={1}"``. 

210 :param kwargs: Any keyword argument. 

211 """ 

212 kwargs["pattern"] = pattern 

213 super().__init_subclass__(*args, **kwargs) 

214 

215 # TODO: the whole class should be marked as abstract 

216 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code 

217 def __new__(cls, *args: Any, **kwargs: Any) -> Self: 

218 """ 

219 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error. 

220 

221 :param args: Any positional arguments. 

222 :param kwargs: Any keyword arguments. 

223 :raises TypeError: When this class gets directly instantiated without being derived to a subclass. 

224 """ 

225 if cls is LongValuedFlagList: 

226 raise TypeError(f"Class '{cls.__name__}' is abstract.") 

227 return super().__new__(cls, *args, **kwargs) 

228 

229 

230@export 

231class WindowsValuedFlagList(ValuedFlagList, pattern="/{0}:{1}"): 

232 """ 

233 Represents a :py:class:`ValuedFlagArgument` with a single slash. 

234 

235 **Example:** 

236 

237 * ``/file:file1.log /file:file2.log`` 

238 """ 

239 

240 # TODO: Is it possible to copy the doc-string from super? 

241 def __init_subclass__(cls, *args: Any, pattern: str = "/{0}:{1}", **kwargs: Any) -> None: 

242 """ 

243 This method is called when a class is derived. 

244 

245 :param args: Any positional arguments. 

246 :param pattern: This pattern is used to format an argument. |br| 

247 Default: ``"/{0}:{1}"``. 

248 :param kwargs: Any keyword argument. 

249 """ 

250 kwargs["pattern"] = pattern 

251 super().__init_subclass__(*args, **kwargs) 

252 

253 # TODO: the whole class should be marked as abstract 

254 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code 

255 def __new__(cls, *args: Any, **kwargs: Any) -> Self: 

256 """ 

257 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error. 

258 

259 :param args: Any positional arguments. 

260 :param kwargs: Any keyword arguments. 

261 :raises TypeError: When this class gets directly instantiated without being derived to a subclass. 

262 """ 

263 if cls is WindowsValuedFlagList: 

264 raise TypeError(f"Class '{cls.__name__}' is abstract.") 

265 return super().__new__(cls, *args, **kwargs)