Coverage for pyTooling/CLIAbstraction/KeyValueFlag.py: 88%

66 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-22 21:29 +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""" 

33Flag arguments represent simple boolean values by being present or absent. 

34 

35.. seealso:: 

36 

37 :mod:`~pyTooling.CLIAbstraction.BooleanFlag` 

38 |rarr| For flags with a different pattern based on the boolean value itself. 

39 :mod:`~pyTooling.CLIAbstraction.ValuedFlag` 

40 |rarr| For flags with a value. 

41 :class:`~pyTooling.CLIAbstraction.OptionalValuedFlag.OptionalValuedFlag` 

42 |rarr| For flags that have an optional value. 

43""" 

44from typing import Union, Iterable, cast, Any, Optional as Nullable 

45from pyTooling.Decorators import export 

46from pyTooling.MetaClasses import abstractclass 

47from pyTooling.Common import getFullyQualifiedName 

48from pyTooling.CLIAbstraction.Argument import NamedAndValuedArgument 

49 

50 

51@export 

52@abstractclass 

53class NamedKeyValuePairsArgument(NamedAndValuedArgument[str], pattern="{0}{1}={2}"): 

54 """ 

55 Class and base-class for all KeyValueFlag classes, which represents a flag argument with key and value 

56 (key-value-pairs). 

57 

58 An optional valued flag is a flag name followed by a value. The default delimiter sign is equal (``=``). Name and 

59 value are passed as one argument to the executable even if the delimiter sign is a whitespace character. If the value 

60 is None, no delimiter sign and value is passed. 

61 

62 **Example:** 

63 

64 * ``-gWidth=100`` 

65 """ 

66 

67 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "{0}{1}={2}", **kwargs: Any) -> None: 

68 """ 

69 This method is called when a class is derived. 

70 

71 :param args: Any positional arguments. 

72 :param name: Optional, name of the CLI argument. 

73 :param pattern: Optional, this pattern is used to format an argument. |br| 

74 Default: ``"{0}{1}={2}"``. 

75 :param kwargs: Any keyword argument. 

76 """ 

77 kwargs["name"] = name 

78 kwargs["pattern"] = pattern 

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

80 

81 def __init__(self, keyValuePairs: dict[str, str]) -> None: 

82 """ 

83 Initialize the argument with a mapping of key-value-pairs, each rendered as its own command line element. 

84 

85 :param keyValuePairs: Key-value-pairs of the argument. 

86 :raises TypeError: If a key or a value is not a string. 

87 """ 

88 super().__init__({}) 

89 

90 for key, value in keyValuePairs.items(): 

91 if not isinstance(key, str): 91 ↛ 92line 91 didn't jump to line 92 because the condition on line 91 was never true

92 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the key is not of type 'str'.") 

93 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.") 

94 raise ex 

95 elif not isinstance(value, str): 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true

96 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the value is not of type 'str'.") 

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

98 raise ex 

99 

100 self._value[key] = value 

101 

102 @property 

103 def Value(self) -> dict[str, str]: 

104 """ 

105 Property to access the internal key-value-pairs (:attr:`_value`). 

106 

107 .. note:: On assignment, the dictionary object is not replaced, but cleared and then reused by adding the given 

108 pairs. 

109 

110 :returns: Internal dictionary of key-value-pairs. 

111 :raises TypeError: If an assigned pair has a key or a value which is not of type string. 

112 """ 

113 return self._value 

114 

115 @Value.setter 

116 def Value(self, keyValuePairs: dict[str, str]) -> None: 

117 innerDict = cast(dict[str, str], self._value) 

118 innerDict.clear() 

119 for key, value in keyValuePairs.items(): 

120 if not isinstance(key, str): 

121 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the key is not of type 'str'.") 

122 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.") 

123 raise ex 

124 elif not isinstance(value, str): 

125 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the value is not of type 'str'.") 

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

127 raise ex 

128 

129 innerDict[key] = value 

130 

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

132 """ 

133 Convert this argument instance to a string representation with proper escaping using the matching pattern based on 

134 the internal name. 

135 

136 :returns: Formatted argument. 

137 :raises ValueError: If internal name is None. 

138 """ 

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

140 raise ValueError(f"Internal value '_name' is None.") 

141 

142 return [self._pattern.format(self._name, key, value) for key, value in self._value.items()] 

143 

144 

145@export 

146@abstractclass 

147class ShortKeyValueFlag(NamedKeyValuePairsArgument, pattern="-{0}{1}={2}"): 

148 """ 

149 Represents a :py:class:`NamedKeyValueFlagArgument` with a single dash in front of the switch name. 

150 

151 **Example:** 

152 

153 * ``-DDEBUG=TRUE`` 

154 """ 

155 

156 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "-{0}{1}={2}", **kwargs: Any) -> None: 

157 """ 

158 This method is called when a class is derived. 

159 

160 :param args: Any positional arguments. 

161 :param name: Optional, name of the CLI argument. 

162 :param pattern: Optional, this pattern is used to format an argument. |br| 

163 Default: ``"-{0}{1}={2}"``. 

164 :param kwargs: Any keyword argument. 

165 """ 

166 kwargs["name"] = name 

167 kwargs["pattern"] = pattern 

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

169 

170 

171@export 

172@abstractclass 

173class LongKeyValueFlag(NamedKeyValuePairsArgument, pattern="--{0}{1}={2}"): 

174 """ 

175 Represents a :py:class:`NamedKeyValueFlagArgument` with a double dash in front of the switch name. 

176 

177 **Example:** 

178 

179 * ``--DDEBUG=TRUE`` 

180 """ 

181 

182 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "--{0}{1}={2}", **kwargs: Any) -> None: 

183 """ 

184 This method is called when a class is derived. 

185 

186 :param args: Any positional arguments. 

187 :param name: Optional, name of the CLI argument. 

188 :param pattern: Optional, this pattern is used to format an argument. |br| 

189 Default: ``"--{0}{1}={2}"``. 

190 :param kwargs: Any keyword argument. 

191 """ 

192 kwargs["name"] = name 

193 kwargs["pattern"] = pattern 

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

195 

196 

197@export 

198@abstractclass 

199class WindowsKeyValueFlag(NamedKeyValuePairsArgument, pattern="/{0}:{1}={2}"): 

200 """ 

201 Represents a :py:class:`NamedKeyValueFlagArgument` with a double dash in front of the switch name. 

202 

203 **Example:** 

204 

205 * ``--DDEBUG=TRUE`` 

206 """ 

207 

208 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "/{0}:{1}={2}", **kwargs: Any) -> None: 

209 """ 

210 This method is called when a class is derived. 

211 

212 :param args: Any positional arguments. 

213 :param name: Optional, name of the CLI argument. 

214 :param pattern: Optional, this pattern is used to format an argument. |br| 

215 Default: ``"/{0}:{1}={2}"``. 

216 :param kwargs: Any keyword argument. 

217 """ 

218 kwargs["name"] = name 

219 kwargs["pattern"] = pattern 

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