Coverage for pyTooling / CLIAbstraction / KeyValueFlag.py: 88%
78 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-08 23:46 +0000
« 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"""
33Flag arguments represent simple boolean values by being present or absent.
35.. seealso::
37 * For flags with different pattern based on the boolean value itself. |br|
38 |rarr| :mod:`~pyTooling.CLIAbstraction.BooleanFlag`
39 * For flags with a value. |br|
40 |rarr| :mod:`~pyTooling.CLIAbstraction.ValuedFlag`
41 * For flags that have an optional value. |br|
42 |rarr| :mod:`~pyTooling.CLIAbstraction.NamedOptionalValuedFlag`
43"""
44from typing import Union, Iterable, Dict, cast, Any, Optional as Nullable, Self
46try:
47 from pyTooling.Decorators import export
48 from pyTooling.Common import getFullyQualifiedName
49 from pyTooling.CLIAbstraction.Argument import NamedAndValuedArgument
50except (ImportError, ModuleNotFoundError): # pragma: no cover
51 print("[pyTooling.Versioning] Could not import from 'pyTooling.*'!")
53 try:
54 from Decorators import export
55 from Common import getFullyQualifiedName
56 from CLIAbstraction.Argument import NamedAndValuedArgument
57 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover
58 print("[pyTooling.Versioning] Could not import directly!")
59 raise ex
62@export
63class NamedKeyValuePairsArgument(NamedAndValuedArgument, pattern="{0}{1}={2}"):
64 """
65 Class and base-class for all KeyValueFlag classes, which represents a flag argument with key and value
66 (key-value-pairs).
68 An optional valued flag is a flag name followed by a value. The default delimiter sign is equal (``=``). Name and
69 value are passed as one argument to the executable even if the delimiter sign is a whitespace character. If the value
70 is None, no delimiter sign and value is passed.
72 **Example:**
74 * ``-gWidth=100``
75 """
77 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "{0}{1}={2}", **kwargs: Any) -> None:
78 """
79 This method is called when a class is derived.
81 :param args: Any positional arguments.
82 :param name: Name of the CLI argument.
83 :param pattern: This pattern is used to format an argument. |br|
84 Default: ``"{0}{1}={2}"``.
85 :param kwargs: Any keyword argument.
86 """
87 kwargs["name"] = name
88 kwargs["pattern"] = pattern
89 super().__init_subclass__(*args, **kwargs)
91 # TODO: the whole class should be marked as abstract
92 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code
93 def __new__(cls, *args: Any, **kwargs: Any) -> Self:
94 """
95 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error.
97 :param args: Any positional arguments.
98 :param kwargs: Any keyword arguments.
99 :raises TypeError: When this class gets directly instantiated without being derived to a subclass.
100 """
101 if cls is NamedKeyValuePairsArgument:
102 raise TypeError(f"Class '{cls.__name__}' is abstract.")
103 return super().__new__(cls, *args, **kwargs)
105 def __init__(self, keyValuePairs: Dict[str, str]) -> None:
106 super().__init__({})
108 for key, value in keyValuePairs.items():
109 if not isinstance(key, str): 109 ↛ 110line 109 didn't jump to line 110 because the condition on line 109 was never true
110 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the key is not of type 'str'.")
111 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.")
112 raise ex
113 elif not isinstance(value, str): 113 ↛ 114line 113 didn't jump to line 114 because the condition on line 113 was never true
114 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the value is not of type 'str'.")
115 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
116 raise ex
118 self._value[key] = value
120 @property
121 def Value(self) -> Dict[str, str]:
122 """
123 Get the internal value.
125 :return: Internal value.
126 """
127 return self._value
129 @Value.setter
130 def Value(self, keyValuePairs: Dict[str, str]) -> None:
131 """
132 Set the internal value.
134 :param keyValuePairs: Value to set.
135 :raises ValueError: If value to set is None.
136 """
137 innerDict = cast(Dict[str, str], self._value)
138 innerDict.clear()
139 for key, value in keyValuePairs.items():
140 if not isinstance(key, str):
141 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the key is not of type 'str'.")
142 ex.add_note(f"Got type '{getFullyQualifiedName(key)}'.")
143 raise ex
144 elif not isinstance(value, str):
145 ex = TypeError(f"Parameter 'keyValuePairs' contains a pair, where the value is not of type 'str'.")
146 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
147 raise ex
149 innerDict[key] = value
151 def AsArgument(self) -> Union[str, Iterable[str]]:
152 """
153 Convert this argument instance to a string representation with proper escaping using the matching pattern based on
154 the internal name.
156 :return: Formatted argument.
157 :raises ValueError: If internal name is None.
158 """
159 if self._name is None: 159 ↛ 160line 159 didn't jump to line 160 because the condition on line 159 was never true
160 raise ValueError(f"Internal value '_name' is None.")
162 return [self._pattern.format(self._name, key, value) for key, value in self._value.items()]
165@export
166class ShortKeyValueFlag(NamedKeyValuePairsArgument, pattern="-{0}{1}={2}"):
167 """
168 Represents a :py:class:`NamedKeyValueFlagArgument` with a single dash in front of the switch name.
170 **Example:**
172 * ``-DDEBUG=TRUE``
173 """
175 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "-{0}{1}={2}", **kwargs: Any) -> None:
176 """
177 This method is called when a class is derived.
179 :param args: Any positional arguments.
180 :param name: Name of the CLI argument.
181 :param pattern: This pattern is used to format an argument. |br|
182 Default: ``"-{0}{1}={2}"``.
183 :param kwargs: Any keyword argument.
184 """
185 kwargs["name"] = name
186 kwargs["pattern"] = pattern
187 super().__init_subclass__(*args, **kwargs)
189 # TODO: the whole class should be marked as abstract
190 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code
191 def __new__(cls, *args: Any, **kwargs: Any) -> Self:
192 """
193 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error.
195 :param args: Any positional arguments.
196 :param kwargs: Any keyword arguments.
197 :raises TypeError: When this class gets directly instantiated without being derived to a subclass.
198 """
199 if cls is ShortKeyValueFlag:
200 raise TypeError(f"Class '{cls.__name__}' is abstract.")
201 return super().__new__(cls, *args, **kwargs)
204@export
205class LongKeyValueFlag(NamedKeyValuePairsArgument, pattern="--{0}{1}={2}"):
206 """
207 Represents a :py:class:`NamedKeyValueFlagArgument` with a double dash in front of the switch name.
209 **Example:**
211 * ``--DDEBUG=TRUE``
212 """
214 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "--{0}{1}={2}", **kwargs: Any) -> None:
215 """
216 This method is called when a class is derived.
218 :param args: Any positional arguments.
219 :param name: Name of the CLI argument.
220 :param pattern: This pattern is used to format an argument. |br|
221 Default: ``"--{0}{1}={2}"``.
222 :param kwargs: Any keyword argument.
223 """
224 kwargs["name"] = name
225 kwargs["pattern"] = pattern
226 super().__init_subclass__(*args, **kwargs)
228 # TODO: the whole class should be marked as abstract
229 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code
230 def __new__(cls, *args: Any, **kwargs: Any) -> Self:
231 """
232 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error.
234 :param args: Any positional arguments.
235 :param kwargs: Any keyword arguments.
236 :raises TypeError: When this class gets directly instantiated without being derived to a subclass.
237 """
238 if cls is LongKeyValueFlag:
239 raise TypeError(f"Class '{cls.__name__}' is abstract.")
240 return super().__new__(cls, *args, **kwargs)
243@export
244class WindowsKeyValueFlag(NamedKeyValuePairsArgument, pattern="/{0}:{1}={2}"):
245 """
246 Represents a :py:class:`NamedKeyValueFlagArgument` with a double dash in front of the switch name.
248 **Example:**
250 * ``--DDEBUG=TRUE``
251 """
253 def __init_subclass__(cls, *args: Any, name: Nullable[str] = None, pattern: str = "/{0}:{1}={2}", **kwargs: Any) -> None:
254 """
255 This method is called when a class is derived.
257 :param args: Any positional arguments.
258 :param name: Name of the CLI argument.
259 :param pattern: This pattern is used to format an argument. |br|
260 Default: ``"/{0}:{1}={2}"``.
261 :param kwargs: Any keyword argument.
262 """
263 kwargs["name"] = name
264 kwargs["pattern"] = pattern
265 super().__init_subclass__(*args, **kwargs)
267 # TODO: the whole class should be marked as abstract
268 # TODO: a decorator should solve the issue and overwrite the __new__ method with that code
269 def __new__(cls, *args: Any, **kwargs: Any) -> Self:
270 """
271 Check if this class was directly instantiated without being derived to a subclass. If so, raise an error.
273 :param args: Any positional arguments.
274 :param kwargs: Any keyword arguments.
275 :raises TypeError: When this class gets directly instantiated without being derived to a subclass.
276 """
277 if cls is LongKeyValueFlag: 277 ↛ 278line 277 didn't jump to line 278 because the condition on line 277 was never true
278 raise TypeError(f"Class '{cls.__name__}' is abstract.")
279 return super().__new__(cls, *args, **kwargs)