Coverage for torxtools/argtools.py: 53%
75 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 22:02 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 22:02 +0000
1"""
2Parser types for command-line options, arguments and sub-commands
4"""
6# Class names are not CamelCase since they act as functions
7# pylint: disable=invalid-name
9import os
10from argparse import Action, ArgumentTypeError
12__all__ = [
13 "is_dir",
14 "is_file",
15 "is_file_and_not_dir",
16 "is_int_between",
17 "is_int_negative",
18 "is_int_negative_or_zero",
19 "is_int_positive",
20 "is_int_positive_or_zero",
21 "is_not_dir",
22]
25def _get_int_number(value: int, message: str) -> int:
26 try:
27 return int(value)
28 except (ValueError, TypeError):
29 raise ArgumentTypeError(message) from None
32class is_int_positive(Action):
33 """
34 Verify that argument passed is a positive integer.
36 Example
37 -------
39 .. code-block::
41 parser.add_argument(
42 "--size", "-s",
43 dest="size",
44 help="[MB] Minimal size of attachment",
45 action=argtools.is_int_positive,
46 default=100,
47 )
48 """
50 def __call__(self, _parser, namespace, value, *args, **kwargs):
51 message = f"value '{value}' must be positive"
52 number = _get_int_number(value, message)
53 if number <= 0:
54 raise ArgumentTypeError(message) from None
55 setattr(namespace, self.dest, number)
58class is_int_positive_or_zero(Action):
59 """
60 Verify that argument passed is a positive integer or zero.
62 Example
63 -------
65 .. code-block::
67 parser.add_argument(
68 "--size", "-s",
69 dest="size",
70 help="[MB] Minimal size of attachment",
71 action=argtools.is_int_positive_or_zero,
72 default=100,
73 )
74 """
76 def __call__(self, _parser, namespace, value, *args, **kwargs):
77 message = f"value '{value}' must be positive or zero"
78 number = _get_int_number(value, message)
79 if number < 0:
80 raise ArgumentTypeError(message) from None
81 setattr(namespace, self.dest, number)
84class is_int_negative(Action):
85 """
86 Verify that argument passed is a negative integer.
88 Example
89 -------
91 .. code-block::
93 parser.add_argument(
94 "--temperature", "-t",
95 dest="temperature",
96 help="[C] Temperature colder than freezing point",
97 action=argtools.is_int_negative,
98 default=-50,
99 )
100 """
102 def __call__(self, _parser, namespace, value, *args, **kwargs):
103 message = f"value '{value}' must be negative"
104 number = _get_int_number(value, message)
105 if number >= 0:
106 raise ArgumentTypeError(message) from None
107 setattr(namespace, self.dest, number)
110class is_int_negative_or_zero(Action):
111 """
112 Verify that argument passed is a negative integer or zero.
114 Example
115 -------
117 .. code-block::
119 parser.add_argument(
120 "--temperature", "-t",
121 dest="temperature",
122 help="[C] Temperature colder than freezing point",
123 action=argtools.is_int_negative_or_zero,
124 default=-50,
125 )
126 """
128 def __call__(self, _parser, namespace, value, *args, **kwargs):
129 message = f"value '{value}' must be negative or zero"
130 number = _get_int_number(value, message)
131 if number > 0:
132 raise ArgumentTypeError(message) from None
133 setattr(namespace, self.dest, number)
136class is_file(Action):
137 """
138 Returns path if path is an existing regular file.
139 This follows symbolic links
141 Example
142 -------
144 .. code-block::
146 parser.add_argument(
147 "-f", "--file"
148 action=argtools.is_file
149 )
150 """
152 def __call__(self, _parser, namespace, value, *args, **kwargs):
153 message = f"value '{value}' must be an existing file"
154 if not os.path.isfile(str(value)):
155 raise ArgumentTypeError(message) from None
156 setattr(namespace, self.dest, value)
159class is_not_dir(Action):
160 """
161 :deprecated: Since 1.1.3
163 Use is_file_and_not_dir instead.
164 In the next minor version, this function will only check that it's not a directory, a no longer check if it's a file or not
166 Example
167 -------
169 .. code-block::
171 parser.add_argument(
172 "-f", "--file"
173 action=argtools.is_not_dir
174 )
175 """
177 def __call__(self, _parser, namespace, value, *args, **kwargs):
178 message = f"value '{value}' must be an existing file"
179 value = str(value)
180 if not os.path.exists(value):
181 raise ArgumentTypeError(message) from None
182 if os.path.isdir(value):
183 raise ArgumentTypeError(message) from None
184 setattr(namespace, self.dest, value)
187class is_file_and_not_dir(Action):
188 """
189 Path must exist and be a file and not a directory.
191 Example
192 -------
194 .. code-block::
196 parser.add_argument(
197 "-f", "--file"
198 action=argtools.is_file_and_not_dir
199 )
200 """
202 def __call__(self, _parser, namespace, value, *args, **kwargs):
203 message = f"value '{value}' must be an existing file"
204 value = str(value)
205 if not os.path.exists(value):
206 raise ArgumentTypeError(message) from None
207 if os.path.isdir(value):
208 raise ArgumentTypeError(message) from None
209 setattr(namespace, self.dest, value)
212class is_dir(Action):
213 """
214 Returns path if path is an existing regular directory.
215 This follows symbolic links
217 Example
218 -------
220 .. code-block::
222 parser.add_argument(
223 "-d", "--dir"
224 action=argtools.is_dir
225 )
226 """
228 def __call__(self, _parser, namespace, value, *args, **kwargs):
229 message = f"value '{value}' must be an existing directory"
230 if not os.path.isdir(str(value)):
231 raise ArgumentTypeError(message) from None
232 setattr(namespace, self.dest, value)
235def is_int_between(minv, maxv):
236 """
237 Verify that argument passed is between minv and maxv (inclusive)
239 Parameters
240 ----------
241 minv: int
242 minimum value
244 maxv: int
245 maximum value
247 Example
248 -------
250 .. code-block::
252 parser.add_argument(
253 "--percent",
254 help="Percentage (0 to 100)",
255 action=argtools.is_int_between(0, 100),
256 default=50,
257 )
258 """
260 class _is_int_between(Action):
261 def __call__(self, _parser, namespace, value, *args, **kwargs):
262 message = f"value '{value}' must be between {minv} and {maxv}"
263 number = _get_int_number(value, message)
264 if number < minv or number > maxv:
265 raise ArgumentTypeError(message) from None
266 setattr(namespace, self.dest, number)
268 return _is_int_between