Coverage for pesummary/core/cli/actions.py: 58.5%
188 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-12-09 22:34 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-12-09 22:34 +0000
1# Licensed under an MIT style license -- see LICENSE.md
3import re
4import copy
5import os
6import ast
7import argparse
8import configparser
9import numpy as np
11__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"]
14class CheckFilesExistAction(argparse.Action):
15 """Class to extend the argparse.Action to identify if files exist
16 """
17 def __call__(self, parser, namespace, values, option_string=None):
18 setattr(namespace, self.dest, values)
19 self.check_input(values)
21 def check_input(self, value):
22 """Check that all files provided exist
24 Parameters
25 ----------
26 value: str, list, dict
27 data structure that you wish to check
28 """
29 if isinstance(value, list):
30 for ff in value:
31 _ = self.check_input(ff)
32 elif isinstance(value, str):
33 _ = self._is_file(value)
34 elif isinstance(value, dict):
35 for _value in value.values():
36 _ = self.check_input(_value)
37 else:
38 _ = self._is_file(value)
40 def _is_file(self, ff):
41 """Return True if the file exists else raise a FileNotFoundError
42 exception
44 Parameters
45 ----------
46 ff: str
47 path to file you wish to check
48 """
49 cond = any(_str in ff for _str in ["*", "@", "https://"])
50 cond2 = isinstance(ff, str) and ff.lower() == "none"
51 if not os.path.isfile(ff) and not cond and not cond2:
52 raise FileNotFoundError(
53 "The file '{}' provided for '{}' does not exist".format(
54 ff, self.dest
55 )
56 )
57 return True
60class BaseDeprecatedAction(object):
61 """Class to handle deprecated argparse options
62 """
63 class _BaseDeprecatedAction(object):
64 def __call__(self, *args, **kwargs):
65 import warnings
66 msg = (
67 "The option '{}' is out-of-date and may not be supported in "
68 "future releases.".format(self.option_strings[0])
69 )
70 if _new_option is not None:
71 msg += " Please use '{}'".format(_new_option)
72 warnings.warn(msg)
73 return super().__call__(*args, **kwargs)
75 def __new__(cls, *args, new_option=None, **kwargs):
76 global _new_option
77 _new_option = new_option
80class DeprecatedStoreAction(BaseDeprecatedAction):
81 """Class to handle deprecated argparse._StoreAction options
82 """
83 class _DeprecatedStoreAction(
84 BaseDeprecatedAction._BaseDeprecatedAction, argparse._StoreAction
85 ):
86 pass
88 def __new__(cls, *args, **kwargs):
89 super().__new__(cls, *args, **kwargs)
90 return cls._DeprecatedStoreAction
93class DeprecatedStoreTrueAction(BaseDeprecatedAction):
94 """Class to handle deprecated argparse._StoreTrueAction options
95 """
96 class _DeprecatedStoreTrueAction(
97 BaseDeprecatedAction._BaseDeprecatedAction, argparse._StoreTrueAction
98 ):
99 pass
101 def __new__(cls, *args, **kwargs):
102 super().__new__(cls, *args, **kwargs)
103 return cls._DeprecatedStoreTrueAction
106class DeprecatedStoreFalseAction(BaseDeprecatedAction):
107 """Class to handle deprecated argparse._StoreFalseAction options
108 """
109 class _DeprecatedStoreFalseAction(
110 BaseDeprecatedAction._BaseDeprecatedAction, argparse._StoreFalseAction
111 ):
112 pass
114 def __new__(cls, *args, **kwargs):
115 super().__new__(cls, *args, **kwargs)
116 return cls._DeprecatedStoreFalseAction
119class ConfigAction(argparse.Action):
120 """Class to extend the argparse.Action to handle dictionaries as input
121 """
122 def __call__(self, parser, namespace, values, option_string=None):
123 setattr(namespace, self.dest, values)
125 items = {}
126 config = configparser.ConfigParser()
127 config.optionxform = str
128 try:
129 config.read(values)
130 sections = config.sections()
131 for section in sections:
132 for key, value in config.items(section):
133 if value.lower() == "true":
134 items[key] = True
135 elif value.lower() == "false":
136 items[key] = False
137 elif value.lower() == "none":
138 items[key] = None
139 else:
140 try:
141 _type = getattr(
142 parser, "_option_string_actions"
143 )["--{}".format(key)].type
144 except Exception:
145 _type = None
146 if ":" in value or "{" in value:
147 try:
148 items[key] = self.dict_from_str(value, dtype=_type)
149 except Exception:
150 items[key] = value
151 elif "," in value or "[" in value:
152 items[key] = self.list_from_str(value, _type)
153 else:
154 if _type is not None:
155 items[key] = _type(value)
156 else:
157 items[key] = value
158 except Exception:
159 pass
160 for i in vars(namespace).keys():
161 if i in items.keys():
162 setattr(namespace, i, items[i])
164 @staticmethod
165 def dict_from_str(string, delimiter=":", dtype=None):
166 """Reformat the string into a dictionary
168 Parameters
169 ----------
170 string: str
171 string that you would like reformatted into a dictionary
172 """
173 string = string.replace("'", "")
174 string = string.replace('"', '')
175 string = string.replace("=", ":")
176 string = string.replace(delimiter, ":")
177 if "dict(" in string:
178 string = string.replace("dict(", "{")
179 string = string.replace(")", "}")
180 string = string.replace(" ", "")
181 string = re.sub(r'([A-Za-z/\.0-9][^\[\],:"}]*)', r'"\g<1>"', string)
182 string = string.replace('""', '"')
183 try:
184 mydict = ast.literal_eval(string)
185 except ValueError as e:
186 pass
187 for key in mydict:
188 if isinstance(mydict[key], str) and mydict[key].lower() == "true":
189 mydict[key] = True
190 elif isinstance(mydict[key], str) and mydict[key].lower() == "false":
191 mydict[key] = False
192 else:
193 try:
194 mydict[key] = int(mydict[key])
195 except ValueError:
196 try:
197 mydict[key] = float(mydict[key])
198 except ValueError:
199 mydict[key] = mydict[key]
200 if dtype is not None:
201 mydict[key] = dtype(mydict[key])
202 return mydict
204 @staticmethod
205 def list_from_str(string, dtype=None):
206 """Reformat the string into a list
208 Parameters
209 ----------
210 string: str
211 string that you would like reformatted into a list
212 """
213 list = []
214 string = string.replace("'", "")
215 if "[" in string:
216 string = string.replace("[", "")
217 if "]" in string:
218 string = string.replace("]", "")
219 if ", " in string:
220 list = string.split(", ")
221 elif "," in string:
222 list = string.split(",")
223 else:
224 list = [string]
225 if dtype is not None:
226 list = [dtype(_) for _ in list]
227 return list
230class DictionaryAction(argparse.Action):
231 """Class to extend the argparse.Action to handle dictionaries as input
232 """
233 def __call__(self, parser, namespace, values, option_string=None):
234 bool = [True if ':' in value else False for value in values]
235 if all(i is True for i in bool):
236 setattr(namespace, self.dest, {})
237 elif all(i is False for i in bool):
238 setattr(namespace, self.dest, [])
239 else:
240 raise ValueError("Did not understand input")
242 items = getattr(namespace, self.dest)
243 items = copy.copy(items)
244 for value in values:
245 value = value.split(':')
246 if len(value) > 2:
247 value = [":".join(value[:-1]), value[-1]]
248 if len(value) == 2:
249 if value[0] in items.keys():
250 if not isinstance(items[value[0]], list):
251 items[value[0]] = [items[value[0]]]
252 items[value[0]].append(value[1])
253 else:
254 items[value[0]] = value[1]
255 elif len(value) == 1:
256 items.append(value[0])
257 else:
258 raise ValueError("Did not understand input")
259 setattr(namespace, self.dest, items)
262class DelimiterSplitAction(argparse.Action):
263 """Class to extend the argparse.Action to handle inputs which need to be split with
264 with a provided delimiter
265 """
266 def __call__(self, parser, namespace, values, option_string=None):
267 import sys
269 args = np.array(sys.argv[1:])
270 cond1 = "--delimiter" in args
271 cond2 = False
272 if cond1:
273 cond2 = (
274 float(np.argwhere(args == "--delimiter"))
275 > float(np.argwhere(args == self.option_strings[0]))
276 )
277 if cond1 and cond2:
278 raise ValueError(
279 "Please provide the '--delimiter' command line argument "
280 "before the '{}' argument".format(self.option_strings[0])
281 )
282 delimiter = namespace.delimiter
283 items = {}
284 for value in values:
285 value = value.split(delimiter)
286 if len(value) > 2:
287 raise ValueError(
288 "'{}' appears multiple times. Please choose a different "
289 "delimiter".format(delimiter)
290 )
291 if value[0] in items.keys() and not isinstance(items[value[0]], list):
292 items[value[0]] = [items[value[0]]]
293 if value[0] in items.keys():
294 items[value[0]].append(value[1])
295 elif len(value) == 1 and len(values) == 1:
296 items = [value[0]]
297 else:
298 items[value[0]] = value[1]
299 setattr(namespace, self.dest, items)