craceplot 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- craceplot/__init__.py +60 -0
- craceplot/_containers/__init__.py +0 -0
- craceplot/_containers/_core.py +1602 -0
- craceplot/_containers/_draw.py +104 -0
- craceplot/_inst/examples/acotsp/crace-3240078.stdout +838 -0
- craceplot/_inst/examples/acotsp/crace.stderr +8 -0
- craceplot/_inst/examples/acotsp/crace.test +216 -0
- craceplot/_inst/examples/acotsp/crace.train +622 -0
- craceplot/_inst/examples/cats200/crace-2814581.stdout +1665 -0
- craceplot/_inst/examples/cats200/crace.stderr +10 -0
- craceplot/_inst/examples/cats200/crace.test +1010 -0
- craceplot/_inst/examples/cats200/crace.train +655 -0
- craceplot/_plots/__init__.py +8 -0
- craceplot/_plots/_core.py +80 -0
- craceplot/_plots/parameters.py +1805 -0
- craceplot/_plots/quality.py +1406 -0
- craceplot/_scripts/__init__.py +4 -0
- craceplot/_scripts/_main.py +132 -0
- craceplot/_scripts/_utils.py +66 -0
- craceplot/_scripts/main +3 -0
- craceplot/_scripts/open_guide +12 -0
- craceplot/_settings/_description.py +168 -0
- craceplot/_settings/_options.json +299 -0
- craceplot/_utils/__init__.py +0 -0
- craceplot/_utils/_base.py +196 -0
- craceplot/_utils/_const.py +8 -0
- craceplot/_utils/_crace.py +42 -0
- craceplot/_utils/_format.py +191 -0
- craceplot/_vergit.py +2 -0
- craceplot/_version.py +24 -0
- craceplot/_vignettes/guide.ipynb +18943 -0
- craceplot-0.1.0.dist-info/METADATA +369 -0
- craceplot-0.1.0.dist-info/RECORD +61 -0
- craceplot-0.1.0.dist-info/WHEEL +5 -0
- craceplot-0.1.0.dist-info/entry_points.txt +2 -0
- craceplot-0.1.0.dist-info/licenses/LICENSE.md +21 -0
- craceplot-0.1.0.dist-info/top_level.txt +2 -0
- docs/_config.yml +51 -0
- docs/_static/css/custom.css +168 -0
- docs/_static/js/navbar.js +40 -0
- docs/_toc.yml +5 -0
- docs/index.md +4 -0
- docs/references/404.md +14 -0
- docs/references/authors.md +46 -0
- docs/references/citiation.bib +17 -0
- docs/references/functions/index.md +5 -0
- docs/references/functions/param_boxplot.ipynb +19 -0
- docs/references/index.md +44 -0
- docs/references/license.md +21 -0
- docs/references/others/favicon.ico +0 -0
- docs/references/others/favicon_io/about.txt +6 -0
- docs/references/others/favicon_io/android-chrome-192x192.png +0 -0
- docs/references/others/favicon_io/android-chrome-512x512.png +0 -0
- docs/references/others/favicon_io/apple-touch-icon.png +0 -0
- docs/references/others/favicon_io/favicon-16x16.png +0 -0
- docs/references/others/favicon_io/favicon-32x32.png +0 -0
- docs/references/others/favicon_io/site.webmanifest +1 -0
- docs/references/others/logo.png +0 -0
- docs/references/others/logo1.png +0 -0
- docs/references/plots/acotsp-parallelcoord-all.png +0 -0
- docs/requirements.txt +5 -0
|
@@ -0,0 +1,1602 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import ast
|
|
4
|
+
import sys
|
|
5
|
+
import json
|
|
6
|
+
import traceback
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Literal, Union, Optional, get_args, get_origin
|
|
10
|
+
|
|
11
|
+
import craceplot._settings._description as info
|
|
12
|
+
from craceplot._utils._const import WIDTH
|
|
13
|
+
from craceplot._utils._format import format_string
|
|
14
|
+
from craceplot._utils._base import check_type, parse_range_list
|
|
15
|
+
|
|
16
|
+
CPLOT_HOME=os.path.dirname(os.path.dirname(__file__))
|
|
17
|
+
|
|
18
|
+
bold = '\033[1m'
|
|
19
|
+
underline = '\033[4m'
|
|
20
|
+
reset = '\033[0m'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
############################# Error information #############################
|
|
24
|
+
# CplotError #
|
|
25
|
+
# OptionError #
|
|
26
|
+
# ParameterDefinitionError #
|
|
27
|
+
# ParameterValueError #
|
|
28
|
+
# ModelError #
|
|
29
|
+
# FileError #
|
|
30
|
+
# CplotExecutionError #
|
|
31
|
+
# ExitError #
|
|
32
|
+
#############################################################################
|
|
33
|
+
class CplotError(Exception):
|
|
34
|
+
"""Exceptions for crace-plot"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, message="No message provided."):
|
|
37
|
+
super().__init__(message)
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
class OptionError(CplotError):
|
|
41
|
+
"""Exception for option type """
|
|
42
|
+
def __init__(self, message="No message provided."):
|
|
43
|
+
super().__init__(message)
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
class ParameterDefinitionError(CplotError):
|
|
47
|
+
"""Exception for option type """
|
|
48
|
+
def __init__(self, message="No message provided."):
|
|
49
|
+
super().__init__(message)
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
class ParameterValueError(CplotError):
|
|
53
|
+
"""Exception for option type """
|
|
54
|
+
def __init__(self, message="No message provided."):
|
|
55
|
+
super().__init__(message)
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
class ModelError(CplotError):
|
|
59
|
+
"""Exception for option type """
|
|
60
|
+
def __init__(self, message="No message provided."):
|
|
61
|
+
super().__init__(message)
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
class FileError(CplotError):
|
|
65
|
+
""" Errors related to files (not found or invalid"""
|
|
66
|
+
def __init__(self, message="No message provided."):
|
|
67
|
+
super().__init__(message)
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
class CplotExecutionError(CplotError):
|
|
71
|
+
"""
|
|
72
|
+
Errors related to the execution of jobs
|
|
73
|
+
"""
|
|
74
|
+
def __init__(self, message="No message provided."):
|
|
75
|
+
super().__init__(message)
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
class ExitError(CplotError):
|
|
79
|
+
"""Exit without error"""
|
|
80
|
+
def __init__(self, message="No message provided."):
|
|
81
|
+
super().__init__(message)
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
class Errors:
|
|
85
|
+
"""
|
|
86
|
+
Namespace wrapper so users can import all errors via:
|
|
87
|
+
from file import Errors
|
|
88
|
+
raise Errors.Option("...")
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
CplotError = CplotError
|
|
92
|
+
OptionError = OptionError
|
|
93
|
+
ParameterDefinitionError = ParameterDefinitionError
|
|
94
|
+
ParameterValueError = ParameterValueError
|
|
95
|
+
ModelError = ModelError
|
|
96
|
+
FileError = FileError
|
|
97
|
+
CplotExecutionError = CplotExecutionError
|
|
98
|
+
ExitError = ExitError
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
################################ Option rules ###############################
|
|
103
|
+
# _option_decoder #
|
|
104
|
+
# IntegerOption #
|
|
105
|
+
# RealOption #
|
|
106
|
+
# ParameterValueError #
|
|
107
|
+
# StringOption #
|
|
108
|
+
# BooleanOption #
|
|
109
|
+
# FileOption #
|
|
110
|
+
# ExeOption #
|
|
111
|
+
# EnablerOption #
|
|
112
|
+
# ListOption #
|
|
113
|
+
# UnionOption #
|
|
114
|
+
#############################################################################
|
|
115
|
+
def _option_decoder(obj):
|
|
116
|
+
"""
|
|
117
|
+
instantiates the correct class for an option
|
|
118
|
+
:param obj: option (dictionary) obtained from the json settings definition
|
|
119
|
+
:return: an instance of the Option class
|
|
120
|
+
:raises OptionError: if type attribute is not found in obj or if type was not recognized
|
|
121
|
+
"""
|
|
122
|
+
o = None
|
|
123
|
+
if not ('type' in obj):
|
|
124
|
+
raise OptionError("Option has no type.")
|
|
125
|
+
if obj['type'] == 'i':
|
|
126
|
+
o = IntegerOption(obj)
|
|
127
|
+
if obj['type'] == 'r':
|
|
128
|
+
o = RealOption(obj)
|
|
129
|
+
if obj['type'] == 'b':
|
|
130
|
+
o = BooleanOption(obj)
|
|
131
|
+
if obj['type'] == 's':
|
|
132
|
+
o = StringOption(obj)
|
|
133
|
+
if obj['type'] == 'p':
|
|
134
|
+
o = FileOption(obj)
|
|
135
|
+
if obj['type'] == 'x':
|
|
136
|
+
o = ExeOption(obj)
|
|
137
|
+
if obj['type'] == 'e':
|
|
138
|
+
o = EnablerOption(obj)
|
|
139
|
+
if obj['type'] == 'l':
|
|
140
|
+
o = ListOption(obj)
|
|
141
|
+
if obj['type'] == 'u':
|
|
142
|
+
o = UnionOption(obj)
|
|
143
|
+
|
|
144
|
+
if o is None:
|
|
145
|
+
raise OptionError(f"Option cannot be read: {underline}{obj['name']}{reset}")
|
|
146
|
+
|
|
147
|
+
return o
|
|
148
|
+
|
|
149
|
+
class Option(ABC):
|
|
150
|
+
"""
|
|
151
|
+
Class Option handles options of the configurator
|
|
152
|
+
:var general_options: List of variables associated with an Option
|
|
153
|
+
name: option name
|
|
154
|
+
type: option type (see option_types)
|
|
155
|
+
short: short flag
|
|
156
|
+
long: long flag
|
|
157
|
+
section: section (group) to which the option belongs
|
|
158
|
+
vignettes: text for the documentation of the option
|
|
159
|
+
description: general description of the option
|
|
160
|
+
:var option_types: List of option types available
|
|
161
|
+
e: enabler option (activates behaviour)
|
|
162
|
+
i: integer option
|
|
163
|
+
r: real option
|
|
164
|
+
s: string option
|
|
165
|
+
b: boolean option
|
|
166
|
+
p: file path option
|
|
167
|
+
x: script file or function to be parsed. Both should be executable
|
|
168
|
+
u: union option
|
|
169
|
+
|
|
170
|
+
:ivar value: option value
|
|
171
|
+
:ivar default: option default value
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
general_options = ["name", "type", "short", "long", "section", "vignettes", "description", "critical"]
|
|
175
|
+
|
|
176
|
+
option_types = ["e", "i", "r", "s", "b", "p", "x", "l", "u"]
|
|
177
|
+
|
|
178
|
+
def __init__(self):
|
|
179
|
+
"""
|
|
180
|
+
Creates an Option object
|
|
181
|
+
"""
|
|
182
|
+
self.value = None
|
|
183
|
+
self.default = None
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def _check_general_options(obj):
|
|
187
|
+
"""
|
|
188
|
+
checks that all options in Options.general_options are defined in obj
|
|
189
|
+
:param obj: Dictionary of options read from the json settings file
|
|
190
|
+
:return: none
|
|
191
|
+
:raises OptionError: if an option is not included in obj
|
|
192
|
+
"""
|
|
193
|
+
for key in Option.general_options:
|
|
194
|
+
if not (key in obj.keys()):
|
|
195
|
+
raise OptionError(f"All craceplot options must define attribute {underline}{key}{reset}")
|
|
196
|
+
|
|
197
|
+
def _set_general_options(self, obj):
|
|
198
|
+
"""
|
|
199
|
+
Creates variables in Option.general_options list and assigns
|
|
200
|
+
the values for these variables in obj
|
|
201
|
+
:param obj: Dictionary of variable values, keys are the names
|
|
202
|
+
in Option.general_options
|
|
203
|
+
"""
|
|
204
|
+
for key in Option.general_options:
|
|
205
|
+
if key == "critical":
|
|
206
|
+
setattr(self, key, int(obj[key]))
|
|
207
|
+
elif key in ["vignettes", "description"]:
|
|
208
|
+
setattr(self, key, obj[key])
|
|
209
|
+
else:
|
|
210
|
+
setattr(self, key, obj[key].replace(".", ""))
|
|
211
|
+
|
|
212
|
+
def is_set(self):
|
|
213
|
+
return not (self.value is None)
|
|
214
|
+
|
|
215
|
+
def set_value(self, value):
|
|
216
|
+
"""
|
|
217
|
+
sets the value of the option and validates it
|
|
218
|
+
:param value: the value to be set
|
|
219
|
+
:return: none
|
|
220
|
+
"""
|
|
221
|
+
self.value = self.parse_value(value, True)
|
|
222
|
+
|
|
223
|
+
def is_default(self):
|
|
224
|
+
"""
|
|
225
|
+
Returns True if value assigned is the default value
|
|
226
|
+
"""
|
|
227
|
+
return self.value == self.default
|
|
228
|
+
|
|
229
|
+
@abstractmethod
|
|
230
|
+
def parse_value(self, value: str, check: bool = False):
|
|
231
|
+
"""
|
|
232
|
+
parses the option value from a string
|
|
233
|
+
:param value: string that has the value of the option
|
|
234
|
+
:param check: boolean that indicates if the value must be validated
|
|
235
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
236
|
+
:raises OptionError: if value is not integer
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
def parse_default(self, value: str):
|
|
240
|
+
"""
|
|
241
|
+
parse the default value of the option, no validation is performed
|
|
242
|
+
:param value: string with the default value
|
|
243
|
+
:return: the parsed value (integer) or None if default is None or empty
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
def check_value(self, value):
|
|
247
|
+
"""
|
|
248
|
+
checks a value has the correct type (integer) and is in the option domain
|
|
249
|
+
:param value: value to be checked
|
|
250
|
+
:return: none
|
|
251
|
+
:raises OptionError: if value is no integer o value is not within domain
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
def parse_domain(self, domain: str):
|
|
255
|
+
"""
|
|
256
|
+
parses the domain of an option
|
|
257
|
+
:param domain: string that defines the domain in the format (lower, upper)
|
|
258
|
+
:return: returns a list of size two with first element the lower bound and second element the upper bound [lower, upper]
|
|
259
|
+
:raises OptionError: if the provided domain cannot be parsed, or if the domain is not correct lower > upper
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
class IntegerOption(Option):
|
|
263
|
+
"""
|
|
264
|
+
class the handles integer craceplot options
|
|
265
|
+
:ivar domain: domain of the option as a two element list [lower, upper]
|
|
266
|
+
"""
|
|
267
|
+
def __init__(self, obj):
|
|
268
|
+
"""
|
|
269
|
+
Creates an Integer Option
|
|
270
|
+
:param obj: dictionary of the option read from the json setting file
|
|
271
|
+
"""
|
|
272
|
+
# add general variables
|
|
273
|
+
super()._check_general_options(obj)
|
|
274
|
+
super()._set_general_options(obj)
|
|
275
|
+
|
|
276
|
+
# other variables
|
|
277
|
+
self.domain = self.parse_domain(obj['domain'])
|
|
278
|
+
self.default = self.parse_default(obj['default'])
|
|
279
|
+
self.value = self.default
|
|
280
|
+
|
|
281
|
+
def parse_value(self, value: str, check: bool = False):
|
|
282
|
+
"""
|
|
283
|
+
parses the option value from a string
|
|
284
|
+
:param value: string that has the value of the option
|
|
285
|
+
:param check: boolean that indicates if the value must be validated
|
|
286
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
287
|
+
:raises OptionError: if value is not integer
|
|
288
|
+
"""
|
|
289
|
+
# if value is empty return default
|
|
290
|
+
if value is None or value == "":
|
|
291
|
+
v = self.default
|
|
292
|
+
elif value.lower() == 'none':
|
|
293
|
+
v = self.default
|
|
294
|
+
elif isinstance(value, str):
|
|
295
|
+
try:
|
|
296
|
+
v = eval(value)
|
|
297
|
+
except Exception as e:
|
|
298
|
+
raise ValueError(f"Option {underline}{self.name}{reset} "
|
|
299
|
+
f"must be integer but has value {bold}{value}{bold}")
|
|
300
|
+
if not isinstance(v, int):
|
|
301
|
+
raise OptionError(f"Option {underline}{self.name}{reset} "
|
|
302
|
+
f"must be integer but has value/default: {bold}{value}{reset}")
|
|
303
|
+
else:
|
|
304
|
+
v = value
|
|
305
|
+
|
|
306
|
+
if check:
|
|
307
|
+
self.check_value(v)
|
|
308
|
+
return v
|
|
309
|
+
|
|
310
|
+
def parse_default(self, value: str):
|
|
311
|
+
"""
|
|
312
|
+
parse the default value of the option, no validation is performed
|
|
313
|
+
:param value: string with the default value
|
|
314
|
+
:return: the parsed value (integer) or None if default is None or empty
|
|
315
|
+
"""
|
|
316
|
+
if value is None or value == "":
|
|
317
|
+
return None
|
|
318
|
+
elif isinstance(value, str):
|
|
319
|
+
return eval(value)
|
|
320
|
+
else:
|
|
321
|
+
return value
|
|
322
|
+
|
|
323
|
+
def check_value(self, value):
|
|
324
|
+
"""
|
|
325
|
+
checks a value has the correct type (integer) and is in the option domain
|
|
326
|
+
:param value: value to be checked
|
|
327
|
+
:return: none
|
|
328
|
+
:raises OptionError: if value is no integer o value is not within domain
|
|
329
|
+
"""
|
|
330
|
+
if value == self.default:
|
|
331
|
+
return
|
|
332
|
+
if not isinstance(value, int):
|
|
333
|
+
raise OptionError(f"Option {underline}{self.name}{reset} type error value "
|
|
334
|
+
f"{bold}{str(value)}{reset} is not int")
|
|
335
|
+
elif (value < self.domain[0]) or (value > self.domain[1]):
|
|
336
|
+
raise OptionError(f"Option {underline}{self.name}{reset} value/default: "
|
|
337
|
+
f"{bold}{str(value)}{reset} is not within its integer domain "
|
|
338
|
+
f"({str(self.domain[0])}, {str(self.domain[1])})")
|
|
339
|
+
|
|
340
|
+
def parse_domain(self, domain: str):
|
|
341
|
+
"""
|
|
342
|
+
parses the domain of an option
|
|
343
|
+
:param domain: string that defines the domain in the format (lower, upper)
|
|
344
|
+
:return: returns a list of size two with first element the lower bound and second element the upper bound [lower, upper]
|
|
345
|
+
:raises OptionError: if the provided domain cannot be parsed, or if the domain is not correct lower > upper
|
|
346
|
+
"""
|
|
347
|
+
if not domain: return None
|
|
348
|
+
p = re.compile("(?P<d1>\d+),(?P<d2>\d+)")
|
|
349
|
+
m = p.match(domain.strip("()"))
|
|
350
|
+
if m is None:
|
|
351
|
+
raise OptionError(f"Domain for integer option "
|
|
352
|
+
f"{underline}{self.name}{reset} is not correct : {domain}")
|
|
353
|
+
d = [eval(m.group("d1")), eval(m.group("d2"))]
|
|
354
|
+
if d[0] > d[1]:
|
|
355
|
+
raise OptionError(f"Domain for integer option "
|
|
356
|
+
f"{underline}{self.name}{reset} is not correct : {domain}")
|
|
357
|
+
return d
|
|
358
|
+
|
|
359
|
+
class RealOption(Option):
|
|
360
|
+
"""
|
|
361
|
+
class the handles real valued craceplot options
|
|
362
|
+
|
|
363
|
+
:ivar domain: domain of the option as a two element list [lower, upper]
|
|
364
|
+
"""
|
|
365
|
+
def __init__(self, obj):
|
|
366
|
+
"""
|
|
367
|
+
Creates a Real Option object
|
|
368
|
+
:param obj: dictionary of the option read from the json setting file
|
|
369
|
+
"""
|
|
370
|
+
# add general variables
|
|
371
|
+
super()._check_general_options(obj)
|
|
372
|
+
super()._set_general_options(obj)
|
|
373
|
+
|
|
374
|
+
# other variables
|
|
375
|
+
self.domain = self.parse_domain(obj['domain'])
|
|
376
|
+
self.default = self.parse_default(obj['default'])
|
|
377
|
+
self.value = self.default
|
|
378
|
+
|
|
379
|
+
def parse_value(self, value: str, check: bool = False):
|
|
380
|
+
"""
|
|
381
|
+
parses the option value from a string
|
|
382
|
+
:param value: string that has the value of the option
|
|
383
|
+
:param check: boolean that indicates if the value must be validated
|
|
384
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
385
|
+
:raises OptionError: if value is not float
|
|
386
|
+
"""
|
|
387
|
+
if value is None or value == "":
|
|
388
|
+
return self.default
|
|
389
|
+
if isinstance(value, str):
|
|
390
|
+
try:
|
|
391
|
+
v = float(eval(value))
|
|
392
|
+
except Exception as e:
|
|
393
|
+
raise ValueError(f"Option {underline}{self.name}{reset} "
|
|
394
|
+
f"must be real but has value {bold}{value}{reset}")
|
|
395
|
+
if not isinstance(v, float):
|
|
396
|
+
raise OptionError(f"Option {underline}{self.name}{reset} "
|
|
397
|
+
f"must be real but has value/default: {value}")
|
|
398
|
+
|
|
399
|
+
else:
|
|
400
|
+
v = value
|
|
401
|
+
|
|
402
|
+
if check:
|
|
403
|
+
self.check_value(v)
|
|
404
|
+
return v
|
|
405
|
+
|
|
406
|
+
def parse_default(self, value: str):
|
|
407
|
+
"""
|
|
408
|
+
parse the default value of the option, no validation is performed
|
|
409
|
+
:param value: string with the default value
|
|
410
|
+
:return: the parsed value (float) or None if default is None or empty
|
|
411
|
+
"""
|
|
412
|
+
if value is None or value == "":
|
|
413
|
+
return None
|
|
414
|
+
elif isinstance(value, str):
|
|
415
|
+
return eval(value)
|
|
416
|
+
else:
|
|
417
|
+
return value
|
|
418
|
+
|
|
419
|
+
def check_value(self, value):
|
|
420
|
+
"""
|
|
421
|
+
checks a value has the correct type (float) and is in the option domain
|
|
422
|
+
:param value: value to be checked
|
|
423
|
+
:return: none
|
|
424
|
+
:raises OptionError: if value is not float or value is not within domain
|
|
425
|
+
"""
|
|
426
|
+
if not isinstance(value, float):
|
|
427
|
+
raise OptionError(f"Option {underline}{self.name}{reset} type error value "
|
|
428
|
+
f"{bold}{str(value)}{reset} is not float")
|
|
429
|
+
elif (value < self.domain[0]) or (value > self.domain[1]):
|
|
430
|
+
raise OptionError(f"Option {underline}{self.name}{reset} value/default: "
|
|
431
|
+
f"{bold}{str(value)}{reset} is not within its real domain "
|
|
432
|
+
f"({str(self.domain[0])}, {str(self.domain[1])})")
|
|
433
|
+
|
|
434
|
+
def parse_domain(self, domain: str):
|
|
435
|
+
"""
|
|
436
|
+
parses the domain of an option
|
|
437
|
+
:param domain: string that defines the domain in the format (lower, upper)
|
|
438
|
+
:return: returns a list of size two with first element the lower bound and second element the upper bound [lower, upper]
|
|
439
|
+
:raises OptionError: if the provided domain cannot be parsed, or if the domain is not correct lower > upper
|
|
440
|
+
"""
|
|
441
|
+
if not domain: return None
|
|
442
|
+
p = re.compile("(?P<d1>[0-9]+\.*[0-9]*),(?P<d2>[0-9]+\.*[0-9]*)")
|
|
443
|
+
m = p.match(domain.strip("()"))
|
|
444
|
+
if m is None:
|
|
445
|
+
raise OptionError(f"Domain for real option "
|
|
446
|
+
f"{underline}{self.name}{reset} is not correct :{domain}")
|
|
447
|
+
d = [eval(m.group("d1")), eval(m.group("d2"))]
|
|
448
|
+
if d[0] > d[1]:
|
|
449
|
+
raise OptionError(f"Domain for real option "
|
|
450
|
+
f"{underline}{self.name}{reset} is not correct :{domain}")
|
|
451
|
+
return d
|
|
452
|
+
|
|
453
|
+
class StringOption(Option):
|
|
454
|
+
"""
|
|
455
|
+
class the handles string craceplot options
|
|
456
|
+
|
|
457
|
+
:ivar domain: domain of the option as list [value1, value2, value3,...]
|
|
458
|
+
"""
|
|
459
|
+
def __init__(self, obj):
|
|
460
|
+
"""
|
|
461
|
+
Creates a String Option object
|
|
462
|
+
:param obj: dictionary of the option read from the json setting file
|
|
463
|
+
"""
|
|
464
|
+
# add general variables
|
|
465
|
+
super()._check_general_options(obj)
|
|
466
|
+
super()._set_general_options(obj)
|
|
467
|
+
|
|
468
|
+
# other variables
|
|
469
|
+
# if obj['name'] in ('title', 'fileName', 'keyParameter', 'catx', 'caty'):
|
|
470
|
+
if 'domain' not in obj.keys():
|
|
471
|
+
self.domain = None
|
|
472
|
+
else:
|
|
473
|
+
self.domain = self.parse_domain(obj['domain'])
|
|
474
|
+
self.default = self.parse_default(obj['default'])
|
|
475
|
+
self.value = self.default
|
|
476
|
+
|
|
477
|
+
def parse_value(self, value: str, check: bool = False):
|
|
478
|
+
"""
|
|
479
|
+
parses the option value from a string
|
|
480
|
+
:param value: string that has the value of the option
|
|
481
|
+
:param check: boolean that indicates if the value must be validated
|
|
482
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
483
|
+
"""
|
|
484
|
+
if value is None or value == "":
|
|
485
|
+
return self.default
|
|
486
|
+
v = str(value)
|
|
487
|
+
if check:
|
|
488
|
+
self.check_value(v)
|
|
489
|
+
return v.lower()
|
|
490
|
+
|
|
491
|
+
def parse_default(self, value: str):
|
|
492
|
+
"""
|
|
493
|
+
parse the default value of the option, no validation is performed
|
|
494
|
+
:param value: string with the default value
|
|
495
|
+
:return: the parses value or None if default is None or empty
|
|
496
|
+
"""
|
|
497
|
+
if not value:
|
|
498
|
+
return None
|
|
499
|
+
elif not isinstance(value, str):
|
|
500
|
+
return str(value).lower()
|
|
501
|
+
else:
|
|
502
|
+
return value.lower()
|
|
503
|
+
|
|
504
|
+
def check_value(self, value):
|
|
505
|
+
"""
|
|
506
|
+
checks a value is in the option domain
|
|
507
|
+
:param value: value to be checked
|
|
508
|
+
:return: none
|
|
509
|
+
:raises OptionError: if value is not string or value is not within domain
|
|
510
|
+
"""
|
|
511
|
+
if not isinstance(value, str):
|
|
512
|
+
raise OptionError(f"Option {underline}{self.name}{reset} type error value "
|
|
513
|
+
f"{bold}{value}{reset} is not string")
|
|
514
|
+
elif self.domain is None:
|
|
515
|
+
return
|
|
516
|
+
elif not (value.lower() in self.domain):
|
|
517
|
+
raise OptionError(f"Option {underline}{self.name}{reset} value/default: "
|
|
518
|
+
f"{bold}{str(value)}{reset} is not within the domain ({', '.join(self.domain)})")
|
|
519
|
+
|
|
520
|
+
def parse_domain(self, domain: str):
|
|
521
|
+
"""
|
|
522
|
+
parses the domain of an option
|
|
523
|
+
:param domain: string that defines the domain in the format (value1, value2, value3, ...)
|
|
524
|
+
:return: returns a list of size N with the different values [value1, value2, value3, ..., valueN]
|
|
525
|
+
:raises OptionError: if the provided domain has repeated values or is empty
|
|
526
|
+
"""
|
|
527
|
+
if not domain: return None
|
|
528
|
+
d = domain.strip("()").split(",")
|
|
529
|
+
# TODO: how to validate string craceplot option domains when some options do not have domain
|
|
530
|
+
if d[0] == "":
|
|
531
|
+
return None
|
|
532
|
+
# if d[0]=="":
|
|
533
|
+
# raise OptionError(f"Domain for string option "
|
|
534
|
+
# f"{underline}{self.name}{reset} is empty or cannot be parsed")
|
|
535
|
+
if len(d) > len(set(d)):
|
|
536
|
+
raise OptionError(f"Domain for string option {underline}{self.name}{reset} "
|
|
537
|
+
f"has repeated values in domain: {domain}")
|
|
538
|
+
d = [x.strip().lower() for x in d]
|
|
539
|
+
return d
|
|
540
|
+
|
|
541
|
+
class BooleanOption(Option):
|
|
542
|
+
"""
|
|
543
|
+
class the handles boolean craceplot options
|
|
544
|
+
|
|
545
|
+
"""
|
|
546
|
+
|
|
547
|
+
def __init__(self, obj):
|
|
548
|
+
"""
|
|
549
|
+
Creates a Boolean Option object
|
|
550
|
+
:param obj: dictionary of the option read from the json setting file
|
|
551
|
+
"""
|
|
552
|
+
# add general variables
|
|
553
|
+
super()._check_general_options(obj)
|
|
554
|
+
super()._set_general_options(obj)
|
|
555
|
+
|
|
556
|
+
# other variables
|
|
557
|
+
self.default = self.parse_default(obj['default'])
|
|
558
|
+
self.value = self.default
|
|
559
|
+
|
|
560
|
+
def set_value(self, value: str):
|
|
561
|
+
"""
|
|
562
|
+
sets the value of the option
|
|
563
|
+
:param value: the value to be set
|
|
564
|
+
:return: none
|
|
565
|
+
"""
|
|
566
|
+
self.value = self.parse_value(value)
|
|
567
|
+
|
|
568
|
+
def parse_value(self, value: str):
|
|
569
|
+
"""
|
|
570
|
+
parses the option value from a string
|
|
571
|
+
:param value: string that has the value of the option
|
|
572
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
573
|
+
"""
|
|
574
|
+
if value is None or value == "":
|
|
575
|
+
return self.default
|
|
576
|
+
elif not isinstance(value, bool):
|
|
577
|
+
if isinstance(value, int):
|
|
578
|
+
return bool(value)
|
|
579
|
+
elif value.lower() == "true":
|
|
580
|
+
return bool(True)
|
|
581
|
+
elif value.lower() == "false":
|
|
582
|
+
return bool(False)
|
|
583
|
+
else:
|
|
584
|
+
return bool(eval(value))
|
|
585
|
+
else:
|
|
586
|
+
return value
|
|
587
|
+
|
|
588
|
+
def parse_default(self, value: str):
|
|
589
|
+
"""
|
|
590
|
+
parse the default value of the option
|
|
591
|
+
:param value: string with the default value
|
|
592
|
+
:return: the parse value or None if default is None or empty
|
|
593
|
+
"""
|
|
594
|
+
if value is None or value == "":
|
|
595
|
+
return None
|
|
596
|
+
elif not isinstance(value, bool):
|
|
597
|
+
if isinstance(value, int):
|
|
598
|
+
return bool(value)
|
|
599
|
+
else:
|
|
600
|
+
return bool(eval(value))
|
|
601
|
+
else:
|
|
602
|
+
return value
|
|
603
|
+
|
|
604
|
+
class FileOption(Option):
|
|
605
|
+
"""
|
|
606
|
+
class the handles file and directory path craceplot options
|
|
607
|
+
|
|
608
|
+
"""
|
|
609
|
+
def __init__(self, obj):
|
|
610
|
+
"""
|
|
611
|
+
Creates a File Option object
|
|
612
|
+
:param obj: dictionary of the option read from the json setting file
|
|
613
|
+
"""
|
|
614
|
+
# add general variables
|
|
615
|
+
super()._check_general_options(obj)
|
|
616
|
+
super()._set_general_options(obj)
|
|
617
|
+
|
|
618
|
+
# other variables
|
|
619
|
+
self.input_value = obj['default']
|
|
620
|
+
self.default = self.parse_default(obj['default'])
|
|
621
|
+
self.value = self.default
|
|
622
|
+
|
|
623
|
+
def set_value(self, value: str, check_file: bool = False, check_parent_dir: bool=True):
|
|
624
|
+
"""
|
|
625
|
+
sets the value of the option and validates it
|
|
626
|
+
:param value: the value to be set
|
|
627
|
+
:param check_file: boolean that indicated if the file should be validated
|
|
628
|
+
:return: none
|
|
629
|
+
"""
|
|
630
|
+
self.input_value = value.strip()
|
|
631
|
+
self.value = self.parse_value(value, check_file, check_parent_dir)
|
|
632
|
+
|
|
633
|
+
def parse_value(self, value: str, check_file: bool = False, check_parent_dir: bool = True):
|
|
634
|
+
"""
|
|
635
|
+
parses the option file or directory path from a string.
|
|
636
|
+
:param value: string that has the path
|
|
637
|
+
:param check_file: boolean that indicates the type of validation to be performed:
|
|
638
|
+
- False: only the directory where the file/directory is located should be validated as existing
|
|
639
|
+
- True: the file or directory should be validated as existing
|
|
640
|
+
:return: parsed absolute path. If the value is None or empty, the default value is returned
|
|
641
|
+
"""
|
|
642
|
+
if value is None or value == "":
|
|
643
|
+
return None
|
|
644
|
+
value = os.path.expanduser(value)
|
|
645
|
+
fvalue = os.path.abspath(value)
|
|
646
|
+
self.check_value(fvalue, check_file, check_parent_dir)
|
|
647
|
+
return fvalue
|
|
648
|
+
|
|
649
|
+
def parse_default(self, value: str):
|
|
650
|
+
"""
|
|
651
|
+
parse the default path of the option, no validation is performed
|
|
652
|
+
:param value: string with the default path
|
|
653
|
+
:return: the parsed absolute path or None if default is None or empty
|
|
654
|
+
"""
|
|
655
|
+
if value is None or value == "":
|
|
656
|
+
return None
|
|
657
|
+
fvalue = os.path.abspath(value)
|
|
658
|
+
return fvalue
|
|
659
|
+
|
|
660
|
+
def check_value(self, value, check_file: bool = False, check_parent_dir:bool =True):
|
|
661
|
+
"""
|
|
662
|
+
checks if the file or directory provided exists
|
|
663
|
+
:param value: path to be checked
|
|
664
|
+
:param check_file: boolean that indicates the type of validation to be performed:
|
|
665
|
+
- False: only the directory where the file/directory is located should be validated as existing
|
|
666
|
+
- True: the file or directory should be validated as existing
|
|
667
|
+
:return: none
|
|
668
|
+
:raises OptionError: if the directory/file does not exists
|
|
669
|
+
"""
|
|
670
|
+
if value is None:
|
|
671
|
+
return
|
|
672
|
+
if value == "":
|
|
673
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
674
|
+
f"provided file {value} is not valid.")
|
|
675
|
+
if check_file:
|
|
676
|
+
if not os.path.exists(value):
|
|
677
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
678
|
+
f"provided file {value} does not exist.")
|
|
679
|
+
elif check_parent_dir:
|
|
680
|
+
dir_path = os.path.dirname(value)
|
|
681
|
+
if not os.path.exists(dir_path):
|
|
682
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
683
|
+
f"provided directory {dir_path} does not exist (option value {value})")
|
|
684
|
+
|
|
685
|
+
def exists_file(self):
|
|
686
|
+
"""
|
|
687
|
+
checks if the file or directory set as option value exists
|
|
688
|
+
:return: True if the value file or directory exists, else False
|
|
689
|
+
"""
|
|
690
|
+
if os.path.exists(self.value):
|
|
691
|
+
return True
|
|
692
|
+
return False
|
|
693
|
+
|
|
694
|
+
def check_value_dir(self, writable: bool = False):
|
|
695
|
+
"""
|
|
696
|
+
checks if the parent directory of the file/directory set as option value exists and its writable
|
|
697
|
+
:param writable: boolean that indicates if the directory should be checked for writing permissions
|
|
698
|
+
:return: none
|
|
699
|
+
:raises OptionError: if the directory does not exists or does not have writing permissions
|
|
700
|
+
"""
|
|
701
|
+
dir_path = os.path.dirname(self.value)
|
|
702
|
+
if not os.path.exists(dir_path):
|
|
703
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
704
|
+
f"directory {dir_path} does not exist (option value {self.value})")
|
|
705
|
+
if writable and (not os.access(dir_path, os.W_OK)):
|
|
706
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
707
|
+
f"directory {dir_path} is not writable (option value {self.value})")
|
|
708
|
+
|
|
709
|
+
def check_value_file(self, writable: bool = False):
|
|
710
|
+
"""
|
|
711
|
+
checks if the file set as option value exists and its writable
|
|
712
|
+
:param writable: boolean that indicates if the file should be checked for writing permissions
|
|
713
|
+
:return: none
|
|
714
|
+
:raises OptionError: if the file does not exists or does not have writing permissions
|
|
715
|
+
"""
|
|
716
|
+
if not os.path.exists(self.value):
|
|
717
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
718
|
+
f"provided file {bold}{self.value}{reset} does not exist.")
|
|
719
|
+
if writable and (not os.access(self.value, os.W_OK)):
|
|
720
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
721
|
+
f"provided directory {bold}{self.value}{reset} is not writable")
|
|
722
|
+
|
|
723
|
+
def check_executable(self, executable: bool = False):
|
|
724
|
+
"""
|
|
725
|
+
checks if the file set as option value exists and its executable
|
|
726
|
+
:param executable: boolean that indicates if the file should be checked for executing permissions
|
|
727
|
+
:return: none
|
|
728
|
+
:raises OptionError: if the file does not exists or does not have executing permissions
|
|
729
|
+
"""
|
|
730
|
+
if not os.path.exists(self.value):
|
|
731
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
732
|
+
f"provided file {bold}{self.value}{reset} does not exist.")
|
|
733
|
+
if executable and (not os.access(self.value, os.X_OK)):
|
|
734
|
+
raise OptionError(f"Option {underline}{self.name}{reset}: "
|
|
735
|
+
f"provided directory {bold}{self.value}{reset} is not executable")
|
|
736
|
+
|
|
737
|
+
class ExeOption(Option):
|
|
738
|
+
"""
|
|
739
|
+
class the handles executable file path craceplot options
|
|
740
|
+
|
|
741
|
+
"""
|
|
742
|
+
def __init__(self, obj):
|
|
743
|
+
"""
|
|
744
|
+
Creates and Executable Option
|
|
745
|
+
:param obj: dictionary of the option read from the json setting file
|
|
746
|
+
"""
|
|
747
|
+
# add general variables
|
|
748
|
+
super()._check_general_options(obj)
|
|
749
|
+
super()._set_general_options(obj)
|
|
750
|
+
|
|
751
|
+
# other variables
|
|
752
|
+
self.default = self.parse_default(obj['default'])
|
|
753
|
+
self.value = self.default
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
def parse_value(self, value: str, check: bool = False):
|
|
757
|
+
"""
|
|
758
|
+
parses the option file path from a string.
|
|
759
|
+
:param value: string that has the path
|
|
760
|
+
:param check: boolean that indicates if validation should be performed:
|
|
761
|
+
:return: parsed absolute path. If the value is None or empty, the default value is returned
|
|
762
|
+
"""
|
|
763
|
+
if value is None or value == "":
|
|
764
|
+
return self.default
|
|
765
|
+
if check:
|
|
766
|
+
check_return,value_ = self.check_value(value)
|
|
767
|
+
if check_return:
|
|
768
|
+
return value_
|
|
769
|
+
value = os.path.expanduser(value)
|
|
770
|
+
fvalue = os.path.abspath(value)
|
|
771
|
+
return fvalue
|
|
772
|
+
|
|
773
|
+
def parse_default(self, value: str):
|
|
774
|
+
"""
|
|
775
|
+
parse the default path of the option, no validation is performed
|
|
776
|
+
:param value: string with the default path
|
|
777
|
+
:return: the parsed absolute path or None if default is None or empty
|
|
778
|
+
"""
|
|
779
|
+
if value is None or value == "":
|
|
780
|
+
return None
|
|
781
|
+
return str(value)
|
|
782
|
+
|
|
783
|
+
def check_value(self, value):
|
|
784
|
+
"""
|
|
785
|
+
checks if the file provided exists and it has execution permissions
|
|
786
|
+
:param value: path to be checked
|
|
787
|
+
:return: none
|
|
788
|
+
:raises OptionError: if the file does not exists or does not have execution permissions
|
|
789
|
+
"""
|
|
790
|
+
if re.search('def', value) == None:
|
|
791
|
+
if not os.path.isfile(value):
|
|
792
|
+
raise OptionError(f"Option {underline}{self.name}{reset} "
|
|
793
|
+
f"provided file {value} does not exist.")
|
|
794
|
+
if not os.access(value, os.X_OK):
|
|
795
|
+
raise OptionError(f"Option {underline}{self.name}{reset} "
|
|
796
|
+
f"provided file {value} does not have correct permissions.")
|
|
797
|
+
else:
|
|
798
|
+
try:
|
|
799
|
+
exec(value)
|
|
800
|
+
return True,value
|
|
801
|
+
except SyntaxError:
|
|
802
|
+
raise OptionError(f"Option {underline}{self.name}{reset} "
|
|
803
|
+
f"provided function {value} has syntax errors.")
|
|
804
|
+
|
|
805
|
+
class EnablerOption(Option):
|
|
806
|
+
"""
|
|
807
|
+
class the handles craceplot options that signal execution modes (e.g. --help, --onlytest, --check, --version)
|
|
808
|
+
|
|
809
|
+
"""
|
|
810
|
+
def __init__(self, obj):
|
|
811
|
+
"""
|
|
812
|
+
Creates an Enabler Option object
|
|
813
|
+
:param obj: dictionary of the option read from the json setting file
|
|
814
|
+
"""
|
|
815
|
+
# add general variables
|
|
816
|
+
super()._check_general_options(obj)
|
|
817
|
+
super()._set_general_options(obj)
|
|
818
|
+
|
|
819
|
+
# other variables
|
|
820
|
+
self.value = False
|
|
821
|
+
self.default = False
|
|
822
|
+
|
|
823
|
+
def parse_value(self, value, check = False):
|
|
824
|
+
self.value = value
|
|
825
|
+
|
|
826
|
+
class ListOption(Option):
|
|
827
|
+
"""
|
|
828
|
+
class the handles string crace options in a list
|
|
829
|
+
|
|
830
|
+
:ivar domain: domain of the option as list [value1, value2, value3,...]
|
|
831
|
+
:ivar value: the selection/combination of choices from the list
|
|
832
|
+
"""
|
|
833
|
+
def __init__(self, obj):
|
|
834
|
+
"""
|
|
835
|
+
Creates a String Option object
|
|
836
|
+
:param obj: dictionary of the option read from the json setting file
|
|
837
|
+
"""
|
|
838
|
+
# add general variables
|
|
839
|
+
super()._check_general_options(obj)
|
|
840
|
+
super()._set_general_options(obj)
|
|
841
|
+
|
|
842
|
+
# other variables
|
|
843
|
+
self.domain = self.parse_domain(obj['domain'])
|
|
844
|
+
self.default = self.parse_default(obj['default'])
|
|
845
|
+
self.value = self.default
|
|
846
|
+
|
|
847
|
+
def parse_value(self, value, check: bool = False):
|
|
848
|
+
"""
|
|
849
|
+
parses the option value from a string
|
|
850
|
+
:param value: string that has the value of the option
|
|
851
|
+
:param check: boolean that indicates if the value must be validated
|
|
852
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
853
|
+
"""
|
|
854
|
+
if check:
|
|
855
|
+
self.check_value(value)
|
|
856
|
+
|
|
857
|
+
if isinstance(value, list):
|
|
858
|
+
out = []
|
|
859
|
+
for x in value:
|
|
860
|
+
if isinstance(x, str):
|
|
861
|
+
parts = re.split(r"[, ]+", x.strip())
|
|
862
|
+
else:
|
|
863
|
+
parts = [x]
|
|
864
|
+
for p in parts:
|
|
865
|
+
if isinstance(p, str):
|
|
866
|
+
r = parse_range_list(p.strip())
|
|
867
|
+
if r is not None:
|
|
868
|
+
out.extend(r)
|
|
869
|
+
else:
|
|
870
|
+
out.append(p)
|
|
871
|
+
else:
|
|
872
|
+
out.append(p)
|
|
873
|
+
return out
|
|
874
|
+
|
|
875
|
+
elif isinstance(value, str):
|
|
876
|
+
r = parse_range_list(value.strip())
|
|
877
|
+
if r is not None:
|
|
878
|
+
return r
|
|
879
|
+
return value
|
|
880
|
+
|
|
881
|
+
else:
|
|
882
|
+
raise OptionError(
|
|
883
|
+
f"Option {underline}{self.name}{reset} got wrong value {value}"
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
def parse_default(self, value):
|
|
887
|
+
"""
|
|
888
|
+
parse the default value of the option, no validation is performed
|
|
889
|
+
:param value: string with the default value
|
|
890
|
+
:return: the parses value or None if default is None or empty
|
|
891
|
+
"""
|
|
892
|
+
if value is None or value == "":
|
|
893
|
+
return None
|
|
894
|
+
if isinstance(value, list):
|
|
895
|
+
return value
|
|
896
|
+
if isinstance(value, str):
|
|
897
|
+
r = parse_range_list(value.strip())
|
|
898
|
+
if r is not None:
|
|
899
|
+
return r
|
|
900
|
+
return value
|
|
901
|
+
return value
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
def check_value(self, value):
|
|
905
|
+
"""
|
|
906
|
+
checks a value is in the option domain
|
|
907
|
+
:param value: value to be checked
|
|
908
|
+
:return: none
|
|
909
|
+
:raises OptionError: if value is not string or value is not within domain
|
|
910
|
+
"""
|
|
911
|
+
if len(value) > 0:
|
|
912
|
+
for v in value:
|
|
913
|
+
if self.domain is None:
|
|
914
|
+
return
|
|
915
|
+
elif not (v in self.domain):
|
|
916
|
+
raise OptionError(f"Option {underline}{self.name}{reset} value/default:"
|
|
917
|
+
f"{str(v)} is not within the domain ({','.join(self.domain)})")
|
|
918
|
+
|
|
919
|
+
def parse_domain(self, domain: str):
|
|
920
|
+
"""
|
|
921
|
+
parses the domain of an option
|
|
922
|
+
:param domain: string that defines the domain in the format (value1, value2, value3, ...)
|
|
923
|
+
:return: returns a list of size N with the different values [value1, value2, value3, ..., valueN]
|
|
924
|
+
:raises OptionError: if the provided domain has repeated values or is empty
|
|
925
|
+
"""
|
|
926
|
+
if not domain is None:
|
|
927
|
+
d = domain.strip("()").split(",")
|
|
928
|
+
# TODO: how to validate string crace option domains when some options do not have domain
|
|
929
|
+
if d[0] == "":
|
|
930
|
+
return None
|
|
931
|
+
if len(d) > len(set(d)):
|
|
932
|
+
raise OptionError(f"Domain for string option {underline}{self.name}{reset} "
|
|
933
|
+
f"has repeated values in domain: {domain}")
|
|
934
|
+
d = [x.strip()for x in d]
|
|
935
|
+
return d
|
|
936
|
+
else:
|
|
937
|
+
return None
|
|
938
|
+
|
|
939
|
+
class UnionOption(Option):
|
|
940
|
+
"""
|
|
941
|
+
class the handles string crace options in a union
|
|
942
|
+
|
|
943
|
+
:ivar domain: domain of the option as list [value1, value2, value3,...]
|
|
944
|
+
:ivar value: the selection/combination of choices from the list
|
|
945
|
+
"""
|
|
946
|
+
def __init__(self, obj):
|
|
947
|
+
"""
|
|
948
|
+
Creates a String Option object
|
|
949
|
+
:param obj: dictionary of the option read from the json setting file
|
|
950
|
+
"""
|
|
951
|
+
# add general variables
|
|
952
|
+
super()._check_general_options(obj)
|
|
953
|
+
super()._set_general_options(obj)
|
|
954
|
+
|
|
955
|
+
# other variables
|
|
956
|
+
self.domain = self.parse_domain(obj['domain'])
|
|
957
|
+
self.default = self.parse_default(obj['default'])
|
|
958
|
+
self.value = self.default
|
|
959
|
+
|
|
960
|
+
def parse_domain(self, domain: str):
|
|
961
|
+
"""
|
|
962
|
+
parses the domain of an option
|
|
963
|
+
:param domain: string that defines the domain in the format (value1, value2, value3, ...)
|
|
964
|
+
:return: returns a list of size N with the different values [value1, value2, value3, ..., valueN]
|
|
965
|
+
:raises OptionError: if the provided domain has repeated values or is empty
|
|
966
|
+
"""
|
|
967
|
+
if domain is None:
|
|
968
|
+
raise OptionError(f"Union option {underline}{self.name}{reset} must include {bold}domain{reset}")
|
|
969
|
+
|
|
970
|
+
try:
|
|
971
|
+
new = eval(domain, {"Union": Union,
|
|
972
|
+
"Literal": Literal,
|
|
973
|
+
"Optional": Optional,
|
|
974
|
+
"bool": bool,
|
|
975
|
+
"List": list,
|
|
976
|
+
"list": list,
|
|
977
|
+
"str": str,
|
|
978
|
+
"int": int,
|
|
979
|
+
"float": float,
|
|
980
|
+
"None": None,})
|
|
981
|
+
except:
|
|
982
|
+
raise OptionError(f"Union option {underline}{self.name}{reset} including wrong {bold}domain{reset}")
|
|
983
|
+
|
|
984
|
+
return new
|
|
985
|
+
|
|
986
|
+
def parse_value(self, value, check: bool = False, check_default: bool=False):
|
|
987
|
+
"""
|
|
988
|
+
parses the option value from a string
|
|
989
|
+
:param value: string that has the value of the option
|
|
990
|
+
:param check: boolean that indicates if the value must be validated
|
|
991
|
+
:return: parsed value. If the value is None or empty, the default value is returned
|
|
992
|
+
"""
|
|
993
|
+
if value is None or value == "":
|
|
994
|
+
parsed = None
|
|
995
|
+
else:
|
|
996
|
+
parsed = self._parse_by_typing_domain(value, self.domain)
|
|
997
|
+
|
|
998
|
+
if check:
|
|
999
|
+
self.check_value(parsed, check_default)
|
|
1000
|
+
|
|
1001
|
+
return parsed
|
|
1002
|
+
|
|
1003
|
+
def parse_default(self, value):
|
|
1004
|
+
"""
|
|
1005
|
+
parse the default value of the option, no validation is performed
|
|
1006
|
+
:param value: string with the default value
|
|
1007
|
+
:return: the parses value or None if default is None or empty
|
|
1008
|
+
"""
|
|
1009
|
+
if value is None or value == "":
|
|
1010
|
+
return None
|
|
1011
|
+
return self.parse_value(value, True, True)
|
|
1012
|
+
|
|
1013
|
+
def set_value(self, value: str):
|
|
1014
|
+
"""
|
|
1015
|
+
sets the value of the option
|
|
1016
|
+
:param value: the value to be set
|
|
1017
|
+
:return: none
|
|
1018
|
+
"""
|
|
1019
|
+
self.value = self.parse_value(value, check=True)
|
|
1020
|
+
|
|
1021
|
+
def check_value(self, value, check_default: bool=False):
|
|
1022
|
+
"""
|
|
1023
|
+
checks a value is in the option domain Union[Literal[..], ]
|
|
1024
|
+
:param value: value to be checked
|
|
1025
|
+
:return: none
|
|
1026
|
+
:raises OptionError: if value is not string or value is not within domain
|
|
1027
|
+
"""
|
|
1028
|
+
if value is None or value == "":
|
|
1029
|
+
return None
|
|
1030
|
+
|
|
1031
|
+
if not check_default and value == self.default: return
|
|
1032
|
+
|
|
1033
|
+
check_type(value=value, expected=self.domain, name=self.name)
|
|
1034
|
+
|
|
1035
|
+
def _parse_by_typing_domain(self, value, expected):
|
|
1036
|
+
"""
|
|
1037
|
+
parse value as in the provided Union domain
|
|
1038
|
+
|
|
1039
|
+
:param value: value to be checked
|
|
1040
|
+
:param expected: expected type for the value
|
|
1041
|
+
:return: none
|
|
1042
|
+
"""
|
|
1043
|
+
origin = get_origin(expected)
|
|
1044
|
+
args = get_args(expected)
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
# --------------------------------
|
|
1048
|
+
# Union / Optional
|
|
1049
|
+
# --------------------------------
|
|
1050
|
+
if origin is Union:
|
|
1051
|
+
last_error = None
|
|
1052
|
+
for t in args:
|
|
1053
|
+
try:
|
|
1054
|
+
return self._parse_by_typing_domain(value, t)
|
|
1055
|
+
except Exception as e:
|
|
1056
|
+
last_error = e
|
|
1057
|
+
raise last_error
|
|
1058
|
+
|
|
1059
|
+
# --------------------------------
|
|
1060
|
+
# Literal
|
|
1061
|
+
# --------------------------------
|
|
1062
|
+
if origin is Literal:
|
|
1063
|
+
# case-insensitive for string literals
|
|
1064
|
+
if isinstance(value, str) and all(isinstance(a, str) for a in args):
|
|
1065
|
+
v = value.strip().lower()
|
|
1066
|
+
for a in args:
|
|
1067
|
+
if v == a.lower():
|
|
1068
|
+
# return canonical literal from domain
|
|
1069
|
+
return a
|
|
1070
|
+
|
|
1071
|
+
# fallback: non-str literals (int, bool, etc.)
|
|
1072
|
+
try:
|
|
1073
|
+
v = value
|
|
1074
|
+
if isinstance(value, str):
|
|
1075
|
+
v = ast.literal_eval(value)
|
|
1076
|
+
except Exception:
|
|
1077
|
+
pass
|
|
1078
|
+
|
|
1079
|
+
if v in args:
|
|
1080
|
+
return v
|
|
1081
|
+
|
|
1082
|
+
raise OptionError(
|
|
1083
|
+
f"Option {underline}{self.name}{reset} value {bold}{value}{reset} "
|
|
1084
|
+
f"is not within domain {self.domain}"
|
|
1085
|
+
)
|
|
1086
|
+
|
|
1087
|
+
# --------------------------------
|
|
1088
|
+
# list / list[T]
|
|
1089
|
+
# --------------------------------
|
|
1090
|
+
if origin is list or expected is list:
|
|
1091
|
+
print(f"in parse: {value}, {type(value)}, {origin}, {expected}")
|
|
1092
|
+
if isinstance(value, list):
|
|
1093
|
+
v = value
|
|
1094
|
+
elif isinstance(value, str):
|
|
1095
|
+
r = parse_range_list(value.strip())
|
|
1096
|
+
print("parsed list: ", r)
|
|
1097
|
+
if r is not None:
|
|
1098
|
+
v = r
|
|
1099
|
+
else:
|
|
1100
|
+
try:
|
|
1101
|
+
v = ast.literal_eval(value)
|
|
1102
|
+
if not isinstance(v, list):
|
|
1103
|
+
v = value
|
|
1104
|
+
except Exception:
|
|
1105
|
+
v = value
|
|
1106
|
+
else:
|
|
1107
|
+
v = value
|
|
1108
|
+
|
|
1109
|
+
# not a list -> single element
|
|
1110
|
+
if not isinstance(v, list):
|
|
1111
|
+
if len(args) == 1:
|
|
1112
|
+
return self._parse_by_typing_domain(v, args[0])
|
|
1113
|
+
|
|
1114
|
+
raise OptionError(
|
|
1115
|
+
f"Option {underline}{self.name}{reset} value {bold}{value}{reset} "
|
|
1116
|
+
f"is not within domain {self.domain}"
|
|
1117
|
+
)
|
|
1118
|
+
|
|
1119
|
+
# real list
|
|
1120
|
+
if len(args) == 0:
|
|
1121
|
+
return v
|
|
1122
|
+
|
|
1123
|
+
elem_type = args[0]
|
|
1124
|
+
return [self._parse_by_typing_domain(x, elem_type) for x in v]
|
|
1125
|
+
|
|
1126
|
+
# --------------------------------
|
|
1127
|
+
# None
|
|
1128
|
+
# --------------------------------
|
|
1129
|
+
if expected is type(None):
|
|
1130
|
+
if value is None:
|
|
1131
|
+
return None
|
|
1132
|
+
|
|
1133
|
+
if isinstance(value, str) and value.lower() == "none":
|
|
1134
|
+
return None
|
|
1135
|
+
|
|
1136
|
+
raise OptionError(
|
|
1137
|
+
f"Option {underline}{self.name}{reset} value {bold}{value}{reset} "
|
|
1138
|
+
f"is not within domain {self.domain}"
|
|
1139
|
+
)
|
|
1140
|
+
|
|
1141
|
+
# --------------------------------
|
|
1142
|
+
# basic types
|
|
1143
|
+
# --------------------------------
|
|
1144
|
+
if expected in (str, int, float, bool):
|
|
1145
|
+
|
|
1146
|
+
# already correct type
|
|
1147
|
+
if isinstance(value, expected):
|
|
1148
|
+
return value
|
|
1149
|
+
|
|
1150
|
+
# only parse from string
|
|
1151
|
+
if not isinstance(value, str):
|
|
1152
|
+
return value
|
|
1153
|
+
|
|
1154
|
+
if expected is str:
|
|
1155
|
+
return value
|
|
1156
|
+
|
|
1157
|
+
if expected is int:
|
|
1158
|
+
return int(value)
|
|
1159
|
+
|
|
1160
|
+
if expected is float:
|
|
1161
|
+
return float(value)
|
|
1162
|
+
|
|
1163
|
+
# bool
|
|
1164
|
+
v = value.lower()
|
|
1165
|
+
if v in ("true", "1", "yes", "y"):
|
|
1166
|
+
return True
|
|
1167
|
+
if v in ("false", "0", "no", "n"):
|
|
1168
|
+
return False
|
|
1169
|
+
|
|
1170
|
+
raise OptionError(
|
|
1171
|
+
f"Option {underline}{self.name}{reset} expects bool, got {bold}{value}{reset}"
|
|
1172
|
+
)
|
|
1173
|
+
|
|
1174
|
+
# --------------------------------
|
|
1175
|
+
# other object types (e.g. Colormap)
|
|
1176
|
+
# --------------------------------
|
|
1177
|
+
# Try python literal first
|
|
1178
|
+
if isinstance(value, str):
|
|
1179
|
+
try:
|
|
1180
|
+
v = ast.literal_eval(value)
|
|
1181
|
+
if isinstance(v, expected):
|
|
1182
|
+
return v
|
|
1183
|
+
except Exception:
|
|
1184
|
+
pass
|
|
1185
|
+
|
|
1186
|
+
# If already correct object
|
|
1187
|
+
if isinstance(value, expected):
|
|
1188
|
+
return value
|
|
1189
|
+
|
|
1190
|
+
# last fallback: return as is (will be rejected by check later)
|
|
1191
|
+
return value
|
|
1192
|
+
|
|
1193
|
+
|
|
1194
|
+
exclude_options = ['help', 'man', 'version', 'dpi', 'showfliers', 'showmeans', 'showscale', 'statisticalTest']
|
|
1195
|
+
class ParseOptions:
|
|
1196
|
+
"""
|
|
1197
|
+
Parse arguments from the input
|
|
1198
|
+
"""
|
|
1199
|
+
def from_input(arguments=None, console=False, add=False):
|
|
1200
|
+
options = CplotOptions(arguments=arguments, console=console, add=add)
|
|
1201
|
+
|
|
1202
|
+
return options
|
|
1203
|
+
|
|
1204
|
+
class CplotOptions:
|
|
1205
|
+
"""
|
|
1206
|
+
Class contains the set of options that crace plot defined for the results analysis.
|
|
1207
|
+
The options are defined in a setting file provided by the package.
|
|
1208
|
+
The results are record from an exsiting Crace execution.
|
|
1209
|
+
"""
|
|
1210
|
+
def __init__(self, arguments, console=False, add=False):
|
|
1211
|
+
"""
|
|
1212
|
+
Initialization of an LoadOptions object. Arguments must be provided.
|
|
1213
|
+
|
|
1214
|
+
:param arguments: command line arguments provided by the user (it is not None)
|
|
1215
|
+
"""
|
|
1216
|
+
|
|
1217
|
+
# TODO: option definition file should be set in the module/package.
|
|
1218
|
+
# load options definition from json file
|
|
1219
|
+
package_dir, _ = os.path.split(__file__)
|
|
1220
|
+
filename = os.path.dirname(package_dir) + "/_settings/_options.json"
|
|
1221
|
+
with open(filename, "r") as read_file:
|
|
1222
|
+
all_options = json.loads(read_file.read(), object_hook=_option_decoder)
|
|
1223
|
+
|
|
1224
|
+
# create option variables (default values are put in place)
|
|
1225
|
+
self.options = []
|
|
1226
|
+
for o in all_options:
|
|
1227
|
+
self.options.append(o.name)
|
|
1228
|
+
setattr(self, o.name, o)
|
|
1229
|
+
|
|
1230
|
+
# arguments is the whole line of input
|
|
1231
|
+
self.arguments = arguments
|
|
1232
|
+
|
|
1233
|
+
# load option values
|
|
1234
|
+
try:
|
|
1235
|
+
self._load_options(arguments, console, add)
|
|
1236
|
+
except OptionError as err:
|
|
1237
|
+
print("#\n! There was an error while reading crace options :")
|
|
1238
|
+
print(f"! {err}")
|
|
1239
|
+
print_info()
|
|
1240
|
+
sys.exit(1)
|
|
1241
|
+
except FileError as err:
|
|
1242
|
+
print("#\n! There was an error while reading crace options file :")
|
|
1243
|
+
print(f"! {err}")
|
|
1244
|
+
sys.exit(1)
|
|
1245
|
+
except ExitError as err:
|
|
1246
|
+
if console:
|
|
1247
|
+
pass
|
|
1248
|
+
sys.exit(0)
|
|
1249
|
+
except Exception as err:
|
|
1250
|
+
print("#\n! There was an error: ")
|
|
1251
|
+
print(f"! {err}")
|
|
1252
|
+
err = traceback.format_exc()
|
|
1253
|
+
print(err)
|
|
1254
|
+
sys.exit(1)
|
|
1255
|
+
|
|
1256
|
+
def _load_options(self, arguments, console=False, add=False):
|
|
1257
|
+
"""
|
|
1258
|
+
Load option values from arguments
|
|
1259
|
+
|
|
1260
|
+
:param arguments: command line arguments provided by the user (it is not None here)
|
|
1261
|
+
:return: none
|
|
1262
|
+
"""
|
|
1263
|
+
# check arguments
|
|
1264
|
+
any_arg = False
|
|
1265
|
+
any_other_arg = False
|
|
1266
|
+
for o in self.options:
|
|
1267
|
+
if self._get_option(o).long in arguments or self._get_option(o).short in arguments:
|
|
1268
|
+
any_arg = True
|
|
1269
|
+
if self._get_option(o).name != 'logDir':
|
|
1270
|
+
any_other_arg = True
|
|
1271
|
+
break
|
|
1272
|
+
# if not any_arg:
|
|
1273
|
+
# raise OptionError(f"Missing valid option(s)")
|
|
1274
|
+
|
|
1275
|
+
# check for help command
|
|
1276
|
+
if self.help.long in arguments:
|
|
1277
|
+
print_help_long()
|
|
1278
|
+
raise ExitError("")
|
|
1279
|
+
|
|
1280
|
+
# check for help command
|
|
1281
|
+
if self.help.short in arguments:
|
|
1282
|
+
print_help_short()
|
|
1283
|
+
raise ExitError("")
|
|
1284
|
+
|
|
1285
|
+
# check for version command
|
|
1286
|
+
if self.version.short in arguments:
|
|
1287
|
+
print_version_short()
|
|
1288
|
+
raise ExitError("")
|
|
1289
|
+
|
|
1290
|
+
if self.version.long in arguments:
|
|
1291
|
+
print_version_long()
|
|
1292
|
+
raise ExitError("")
|
|
1293
|
+
|
|
1294
|
+
if self.man.long in arguments or self.man.short in arguments:
|
|
1295
|
+
index = arguments.index(self.man.long) if self.man.long in arguments else arguments.index(self.man.short)
|
|
1296
|
+
if len(arguments) > index+1:
|
|
1297
|
+
if arguments[index+1] not in self.options:
|
|
1298
|
+
raise OptionError(f"{arguments[index+1]} is an incorrect crace option name.")
|
|
1299
|
+
else:
|
|
1300
|
+
print_man(self._get_option(arguments[index+1]))
|
|
1301
|
+
else:
|
|
1302
|
+
raise OptionError(f"One option shoud be provided for man.")
|
|
1303
|
+
raise ExitError("")
|
|
1304
|
+
|
|
1305
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1306
|
+
print("# Reading crace plot options...")
|
|
1307
|
+
print("# Arguments: {}".format(arguments))
|
|
1308
|
+
print("#\n# Loading crace plot options...")
|
|
1309
|
+
|
|
1310
|
+
# check logDir
|
|
1311
|
+
try:
|
|
1312
|
+
if self.logDir.long in arguments:
|
|
1313
|
+
i = arguments.index(self.logDir.long)
|
|
1314
|
+
self.logDir.set_value(arguments[i+1])
|
|
1315
|
+
elif self.logDir.short in arguments:
|
|
1316
|
+
i = arguments.index(self.logDir.short)
|
|
1317
|
+
self.logDir.set_value(arguments[i+1])
|
|
1318
|
+
elif self.logDir.exists_file():
|
|
1319
|
+
print("# Default race_log folder was found in current directory.")
|
|
1320
|
+
else:
|
|
1321
|
+
# in this case, we remove the scenario file and assume parameters
|
|
1322
|
+
# will be handled manually
|
|
1323
|
+
raise OptionError(f"Option {underline}logDir(-l){reset} must be provided.")
|
|
1324
|
+
except Exception as e:
|
|
1325
|
+
if isinstance(e, IndexError):
|
|
1326
|
+
raise OptionError(f"Option {underline}logDir(-l){reset} has no value.")
|
|
1327
|
+
else:
|
|
1328
|
+
raise OptionError(e)
|
|
1329
|
+
|
|
1330
|
+
if self.logDir.value and not self.logDir.exists_file():
|
|
1331
|
+
raise OptionError(f"The provided race_log folder {self.logDir.value} is not readable or does not exist.")
|
|
1332
|
+
|
|
1333
|
+
if self.logDir.is_default():
|
|
1334
|
+
self.logDir.set_value(os.path.dirname(self.logDir.value))
|
|
1335
|
+
|
|
1336
|
+
if console and not any_other_arg:
|
|
1337
|
+
return
|
|
1338
|
+
|
|
1339
|
+
options_long = [self._get_option(x).long for x in self.options]
|
|
1340
|
+
options_short = [self._get_option(x).short for x in self.options]
|
|
1341
|
+
|
|
1342
|
+
for o in self.options:
|
|
1343
|
+
if len(arguments) < 1:
|
|
1344
|
+
break
|
|
1345
|
+
if self._get_option(o).short in arguments:
|
|
1346
|
+
index = arguments.index(self._get_option(o).short)
|
|
1347
|
+
elif self._get_option(o).long in arguments:
|
|
1348
|
+
index = arguments.index(self._get_option(o).long)
|
|
1349
|
+
else:
|
|
1350
|
+
continue
|
|
1351
|
+
|
|
1352
|
+
if isinstance(self._get_option(o), EnablerOption):
|
|
1353
|
+
self._set_option(o, True)
|
|
1354
|
+
del arguments[index]
|
|
1355
|
+
elif isinstance(self._get_option(o), ListOption):
|
|
1356
|
+
s = []
|
|
1357
|
+
start = index
|
|
1358
|
+
end = start + 1
|
|
1359
|
+
while (end < len(arguments) and
|
|
1360
|
+
arguments[end] not in options_long and
|
|
1361
|
+
arguments[end] not in options_short):
|
|
1362
|
+
s.append(arguments[end])
|
|
1363
|
+
end += 1
|
|
1364
|
+
for i in reversed(range(start, end)):
|
|
1365
|
+
del arguments[i]
|
|
1366
|
+
self._set_option(o, s)
|
|
1367
|
+
else:
|
|
1368
|
+
try:
|
|
1369
|
+
self._set_option(o, arguments[index + 1])
|
|
1370
|
+
del arguments[index:(index + 2)]
|
|
1371
|
+
except Exception as e:
|
|
1372
|
+
if isinstance(e, OptionError) or isinstance(e, ValueError):
|
|
1373
|
+
raise OptionError(e)
|
|
1374
|
+
else:
|
|
1375
|
+
raise OptionError(f"{self._get_option(o).name} has no value.")
|
|
1376
|
+
|
|
1377
|
+
if len(arguments) > 0:
|
|
1378
|
+
raise OptionError(f"Argument not recognized: {arguments[0]}")
|
|
1379
|
+
|
|
1380
|
+
if self.fileName.value is None:
|
|
1381
|
+
current_time = datetime.now()
|
|
1382
|
+
formatted_time = current_time.strftime("Plot_%Y%m%d_%H%M%S")
|
|
1383
|
+
self.fileName.value = formatted_time
|
|
1384
|
+
|
|
1385
|
+
def _get_option(self, option_name):
|
|
1386
|
+
"""
|
|
1387
|
+
get a option in the LoadOption object
|
|
1388
|
+
:param option_name: name of the option to be returned
|
|
1389
|
+
:return: value of the option
|
|
1390
|
+
:raises OptionError: if the option name is unknown
|
|
1391
|
+
"""
|
|
1392
|
+
if option_name not in self.options:
|
|
1393
|
+
raise OptionError(f"Attempt to get unknown option {underline}option_name{reset}")
|
|
1394
|
+
return getattr(self, option_name)
|
|
1395
|
+
|
|
1396
|
+
def _set_option(self, option_name, option_value):
|
|
1397
|
+
"""
|
|
1398
|
+
set the value of an option in the LoadOptions object
|
|
1399
|
+
:param option_name: name of the option
|
|
1400
|
+
:param option_value: value to be set
|
|
1401
|
+
:return: none
|
|
1402
|
+
:raises OptionError: if the option name is unknown
|
|
1403
|
+
"""
|
|
1404
|
+
if option_name not in self.options:
|
|
1405
|
+
raise OptionError(f"Attempt to set unknown option {underline}option_name{reset} with value {bold}option_value{reset}")
|
|
1406
|
+
getattr(self, option_name).set_value(option_value)
|
|
1407
|
+
|
|
1408
|
+
def _print_options(self):
|
|
1409
|
+
"""
|
|
1410
|
+
Print all options
|
|
1411
|
+
"""
|
|
1412
|
+
print('# Crace plot options: ')
|
|
1413
|
+
for o in self.options:
|
|
1414
|
+
v = self._get_option(o).value
|
|
1415
|
+
if o not in exclude_options:
|
|
1416
|
+
print("# {}: {}".format(o, v))
|
|
1417
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
################################ Print details ##############################
|
|
1422
|
+
# print_cplot_header #
|
|
1423
|
+
# print_help #
|
|
1424
|
+
# print_help_long #
|
|
1425
|
+
# print_help_short #
|
|
1426
|
+
# print_man #
|
|
1427
|
+
# print_info #
|
|
1428
|
+
# print_version_long #
|
|
1429
|
+
# print_version_short #
|
|
1430
|
+
# print_args #
|
|
1431
|
+
#############################################################################
|
|
1432
|
+
def print_cplot_header():
|
|
1433
|
+
# TODO: make version automatic
|
|
1434
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1435
|
+
print(f'# {info.description}')
|
|
1436
|
+
print(f'# Version: {info.version}')
|
|
1437
|
+
print(f'# {info.copyright}')
|
|
1438
|
+
print('#')
|
|
1439
|
+
print('# Authors: ')
|
|
1440
|
+
for x in info.authors: print(f'# {x}')
|
|
1441
|
+
print('#')
|
|
1442
|
+
print(f'# Contact:\n# {info.contact} ({info.contact_email})')
|
|
1443
|
+
print('#')
|
|
1444
|
+
print(f'# Check more details at {info.url} ')
|
|
1445
|
+
line = format_string(info.license, hanging=0, space=True)
|
|
1446
|
+
print("\n".join(f"# {line}" for line in line.splitlines()))
|
|
1447
|
+
print('#')
|
|
1448
|
+
print(f'# installed at: {CPLOT_HOME}')
|
|
1449
|
+
|
|
1450
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
def print_help():
|
|
1454
|
+
"""
|
|
1455
|
+
Print help information of crace-plot
|
|
1456
|
+
"""
|
|
1457
|
+
print_cplot_header()
|
|
1458
|
+
print("# called with: --help (-h)")
|
|
1459
|
+
package_dir, _ = os.path.split(__file__)
|
|
1460
|
+
filename = os.path.dirname(package_dir) + "/_settings/_options.json"
|
|
1461
|
+
with open(filename, "r") as read_file:
|
|
1462
|
+
all_options = json.loads(read_file.read(), object_hook=_option_decoder)
|
|
1463
|
+
|
|
1464
|
+
options = []
|
|
1465
|
+
for o in all_options:
|
|
1466
|
+
options.append(o.name)
|
|
1467
|
+
if o.short is not None and o.short != "":
|
|
1468
|
+
print("#\n# {:<4}, {:<29}{}".format(o.short, o.long, o.description))
|
|
1469
|
+
else:
|
|
1470
|
+
print("#\n# {:<6}{:<29}{}".format(o.short,o.long, o.description))
|
|
1471
|
+
if o.default is not None or o.default != "":
|
|
1472
|
+
print("# {:<35}Default: {}".format('', o.default))
|
|
1473
|
+
if o.type in ['i', 's']:
|
|
1474
|
+
print("# {:<35}Domain: {}".format('', o.domain))
|
|
1475
|
+
print(f"#\n#{f'':-^{WIDTH-1}}")
|
|
1476
|
+
|
|
1477
|
+
|
|
1478
|
+
def print_help_long():
|
|
1479
|
+
print_cplot_header()
|
|
1480
|
+
package_dir, _ = os.path.split(__file__)
|
|
1481
|
+
filename = os.path.dirname(package_dir) + "/_settings/_options.json"
|
|
1482
|
+
with open(filename, "r") as read_file:
|
|
1483
|
+
all_options = json.loads(read_file.read(), object_hook=_option_decoder)
|
|
1484
|
+
|
|
1485
|
+
from collections import defaultdict
|
|
1486
|
+
groups = defaultdict(list)
|
|
1487
|
+
for o in all_options:
|
|
1488
|
+
if o.critical > 1: continue
|
|
1489
|
+
groups[o.section].append(o)
|
|
1490
|
+
|
|
1491
|
+
options = []
|
|
1492
|
+
for section, options_list in groups.items():
|
|
1493
|
+
print(f"\n {f' {section.upper()} ':=^{WIDTH-4}}\n")
|
|
1494
|
+
|
|
1495
|
+
for o in options_list:
|
|
1496
|
+
if o.critical > 1:
|
|
1497
|
+
continue
|
|
1498
|
+
options.append(o.name)
|
|
1499
|
+
|
|
1500
|
+
print(f"{bold}# Name:{reset} {o.name}")
|
|
1501
|
+
print(f" {bold}Long:{reset} {o.long if o.long else 'None'}")
|
|
1502
|
+
print(f" {bold}Short:{reset} {o.short if o.short else 'None'}")
|
|
1503
|
+
if o.type in ['i', 's']:
|
|
1504
|
+
print(f""" {format_string(f"{bold}Domain:{reset} {o.domain if o.domain else 'None'}", hanging=2)}""")
|
|
1505
|
+
if o.default is not None and o.default != "" and o.default != []:
|
|
1506
|
+
print(f" {bold}Default:{reset} {o.default}")
|
|
1507
|
+
else:
|
|
1508
|
+
print(f" {bold}Default:{reset} None")
|
|
1509
|
+
if o.description:
|
|
1510
|
+
print(f""" {format_string(f"{bold}Description:{reset} {o.description}", hanging=2)}""")
|
|
1511
|
+
if o.vignettes:
|
|
1512
|
+
print(f""" {format_string(f"{bold}Vignettes:{reset} {o.vignettes}", hanging=2, space=True)}""")
|
|
1513
|
+
print()
|
|
1514
|
+
|
|
1515
|
+
print(f'\n# call with {bold}--man [option_name]{reset} to check the details of specified option')
|
|
1516
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
def print_help_short():
|
|
1520
|
+
print_cplot_header()
|
|
1521
|
+
package_dir, _ = os.path.split(__file__)
|
|
1522
|
+
filename = os.path.dirname(package_dir) + "/_settings/_options.json"
|
|
1523
|
+
with open(filename, "r") as read_file:
|
|
1524
|
+
all_options = json.loads(read_file.read(), object_hook=_option_decoder)
|
|
1525
|
+
|
|
1526
|
+
from collections import defaultdict
|
|
1527
|
+
groups = defaultdict(list)
|
|
1528
|
+
for o in all_options:
|
|
1529
|
+
if o.critical > 1: continue
|
|
1530
|
+
groups[o.section].append(o)
|
|
1531
|
+
|
|
1532
|
+
max_name_l = max([len(o.name) for o in all_options if o.critical <= 1])
|
|
1533
|
+
max_short_l = max([len(o.short) for o in all_options if o.critical <= 1])
|
|
1534
|
+
max_long_l = max([len(o.long) for o in all_options if o.critical <= 1])
|
|
1535
|
+
sum_max_l = sum([max_name_l, max_short_l, max_long_l])
|
|
1536
|
+
|
|
1537
|
+
options = []
|
|
1538
|
+
for section, options_list in groups.items():
|
|
1539
|
+
print(f"# {bold}{section}{reset}")
|
|
1540
|
+
|
|
1541
|
+
for o in options_list:
|
|
1542
|
+
if o.critical > 1:
|
|
1543
|
+
continue
|
|
1544
|
+
options.append(o.name)
|
|
1545
|
+
format_de = format_string(o.description, width=WIDTH*1.2, hanging=sum_max_l+5) if o.description else ""
|
|
1546
|
+
print(f" {o.name:<{max_name_l+1}}{o.short:<{max_short_l+1}}{o.long:<{max_long_l+1}}{format_de}")
|
|
1547
|
+
print()
|
|
1548
|
+
print(f'\n# call with {bold}--man [option_name]{reset} to check the details of specified option')
|
|
1549
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
def print_man(o):
|
|
1553
|
+
"""
|
|
1554
|
+
print selected options: critical <= 1
|
|
1555
|
+
"""
|
|
1556
|
+
if o.critical > 1 and o.critical <= 4:
|
|
1557
|
+
print(f"Option {bold}{o.name}{reset} is not available in crace currently.")
|
|
1558
|
+
raise ExitError("")
|
|
1559
|
+
elif o.critical > 4 and o.critical <7:
|
|
1560
|
+
print(f"{bold}{o.name}{reset} is not an available crace option.")
|
|
1561
|
+
raise ExitError("")
|
|
1562
|
+
|
|
1563
|
+
print(f"{bold}Name:{reset} {o.name}")
|
|
1564
|
+
print(f"{bold}Long:{reset} {o.long if o.long else 'None'}")
|
|
1565
|
+
print(f"{bold}Short:{reset} {o.short if o.short else 'None'}")
|
|
1566
|
+
if o.type in ['i', 's']:
|
|
1567
|
+
print(f"""\n{format_string(f"{bold}Domain:{reset} {o.domain if o.domain else 'None'}", hanging=0)}\n""")
|
|
1568
|
+
if o.default is not None and o.default != "" and o.default != []:
|
|
1569
|
+
print(f"{bold}Default:{reset} {o.default}")
|
|
1570
|
+
else:
|
|
1571
|
+
print(f"{bold}Default:{reset} None")
|
|
1572
|
+
print(f"""\n{format_string(f"{bold}Description:{reset} {o.description}", hanging=0)}\n""")
|
|
1573
|
+
print(f"""{format_string(f"{bold}Vignettes:{reset} {o.vignettes}", hanging=0, space=True)}""")
|
|
1574
|
+
|
|
1575
|
+
|
|
1576
|
+
def print_info():
|
|
1577
|
+
print(f"! You can check the information of HELP ({bold}-h, --help{reset}) "
|
|
1578
|
+
f"or man page ({bold}--man / -m{reset}).\n")
|
|
1579
|
+
|
|
1580
|
+
|
|
1581
|
+
def print_version_long():
|
|
1582
|
+
print_cplot_header()
|
|
1583
|
+
print_cite_info()
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
def print_version_short():
|
|
1587
|
+
print(f"crace {info.version}")
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
def print_args(args):
|
|
1591
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1592
|
+
print('# Crace plot arguments: ')
|
|
1593
|
+
for name, value in vars(args).items():
|
|
1594
|
+
if name in ['data', 'options']: continue
|
|
1595
|
+
print(f"# {name}: {value}")
|
|
1596
|
+
print(f"#{f'':-^{WIDTH-1}}")
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
def print_cite_info():
|
|
1600
|
+
cit = format_string(info.citiation, width=WIDTH, hanging=0, space=True)
|
|
1601
|
+
print("\n".join(f"# {line}" for line in cit.splitlines()))
|
|
1602
|
+
print(f"#{f'':-^{WIDTH-1}}")
|