looper 1.7.0a1__py3-none-any.whl → 2.0.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- looper/__main__.py +1 -1
- looper/_version.py +2 -1
- looper/cli_divvy.py +10 -6
- looper/cli_pydantic.py +413 -0
- looper/command_models/DEVELOPER.md +85 -0
- looper/command_models/README.md +4 -0
- looper/command_models/__init__.py +6 -0
- looper/command_models/arguments.py +293 -0
- looper/command_models/commands.py +335 -0
- looper/conductor.py +161 -28
- looper/const.py +9 -0
- looper/divvy.py +56 -47
- looper/exceptions.py +9 -1
- looper/looper.py +196 -168
- looper/pipeline_interface.py +2 -12
- looper/project.py +154 -176
- looper/schemas/pipeline_interface_schema_generic.yaml +14 -6
- looper/utils.py +450 -78
- {looper-1.7.0a1.dist-info → looper-2.0.0.dist-info}/METADATA +24 -14
- {looper-1.7.0a1.dist-info → looper-2.0.0.dist-info}/RECORD +24 -19
- {looper-1.7.0a1.dist-info → looper-2.0.0.dist-info}/WHEEL +1 -1
- {looper-1.7.0a1.dist-info → looper-2.0.0.dist-info}/entry_points.txt +1 -1
- looper/cli_looper.py +0 -788
- {looper-1.7.0a1.dist-info → looper-2.0.0.dist-info}/LICENSE.txt +0 -0
- {looper-1.7.0a1.dist-info → looper-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
"""
|
2
|
+
Argument definitions via a thin wrapper around `pydantic.fields.FieldInfo`
|
3
|
+
"""
|
4
|
+
|
5
|
+
import enum
|
6
|
+
import os
|
7
|
+
from copy import copy
|
8
|
+
from typing import Any, List
|
9
|
+
|
10
|
+
import pydantic.v1 as pydantic
|
11
|
+
|
12
|
+
|
13
|
+
class Argument(pydantic.fields.FieldInfo):
|
14
|
+
"""
|
15
|
+
CLI argument / flag definition
|
16
|
+
|
17
|
+
This class is designed to define CLI arguments or flags. It leverages
|
18
|
+
Pydantic for data validation and serves as a source of truth for multiple
|
19
|
+
interfaces, including a CLI.
|
20
|
+
|
21
|
+
Naively, one would think one could just subclass `pydantic.Field`,
|
22
|
+
but actually `pydantic.Field` is a function, and not a class.
|
23
|
+
`pydantic.Field()` returns a validated `FieldInfo` instance,
|
24
|
+
so we instead subclass `FieldInfo` directly and validate it in the
|
25
|
+
constructor.
|
26
|
+
|
27
|
+
:param str name: argument name, e.g. "ignore-args"
|
28
|
+
:param Any default: a tuple of the form (type, default_value). If the
|
29
|
+
default value is `...` (Ellipsis), then the argument is required.
|
30
|
+
:param str description: argument description, which will appear as the
|
31
|
+
help text for this argument
|
32
|
+
:param dict kwargs: additional keyword arguments supported by
|
33
|
+
`FieldInfo`. These are passed along as they are.
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__(
|
37
|
+
self, name: str, default: Any, description: str, alias: str = None, **kwargs
|
38
|
+
) -> None:
|
39
|
+
self._name = name
|
40
|
+
super().__init__(
|
41
|
+
default=default, description=description, alias=alias, **kwargs
|
42
|
+
)
|
43
|
+
self._validate()
|
44
|
+
|
45
|
+
@property
|
46
|
+
def name(self):
|
47
|
+
"""
|
48
|
+
Argument name as used in the CLI, e.g. "ignore-args"
|
49
|
+
"""
|
50
|
+
return self._name
|
51
|
+
|
52
|
+
def with_reduced_default(self) -> pydantic.fields.FieldInfo:
|
53
|
+
"""
|
54
|
+
Convert to a `FieldInfo` instance with reduced default value
|
55
|
+
|
56
|
+
Returns a copy of an instance, but with the `default` attribute
|
57
|
+
replaced by only the default value, without the type information.
|
58
|
+
This is required when using an instance in a direct `pydantic`
|
59
|
+
model definition, instead of creating a model dynamically using
|
60
|
+
`pydantic.create_model`.
|
61
|
+
|
62
|
+
TODO: this is due to this issue:
|
63
|
+
https://github.com/pydantic/pydantic/issues/2248#issuecomment-757448447
|
64
|
+
and it's a bit tedious.
|
65
|
+
|
66
|
+
"""
|
67
|
+
c = copy(self)
|
68
|
+
_, default_value = self.default
|
69
|
+
c.default = default_value
|
70
|
+
return c
|
71
|
+
|
72
|
+
|
73
|
+
class ArgumentEnum(enum.Enum):
|
74
|
+
"""
|
75
|
+
Lists all available arguments
|
76
|
+
|
77
|
+
Having a single "repository" of arguments allows us to re-use them easily across different commands.
|
78
|
+
|
79
|
+
TODO: not sure whether an enum is the ideal data structure for that
|
80
|
+
"""
|
81
|
+
|
82
|
+
IGNORE_FLAGS = Argument(
|
83
|
+
name="ignore_flags",
|
84
|
+
alias="-i",
|
85
|
+
default=(bool, False),
|
86
|
+
description="Ignore run status flags",
|
87
|
+
)
|
88
|
+
FORCE_YES = Argument(
|
89
|
+
name="force_yes",
|
90
|
+
alias="-f",
|
91
|
+
default=(bool, False),
|
92
|
+
description="Provide upfront confirmation of destruction intent, to skip console query. Default=False",
|
93
|
+
)
|
94
|
+
|
95
|
+
DESCRIBE_CODES = Argument(
|
96
|
+
name="describe_codes",
|
97
|
+
default=(bool, False),
|
98
|
+
description="Show status codes description. Default=False",
|
99
|
+
)
|
100
|
+
|
101
|
+
ITEMIZED = Argument(
|
102
|
+
name="itemized",
|
103
|
+
default=(bool, False),
|
104
|
+
description="Show detailed overview of sample statuses. Default=False",
|
105
|
+
)
|
106
|
+
|
107
|
+
FLAGS = Argument(
|
108
|
+
name="flags",
|
109
|
+
alias="-f",
|
110
|
+
default=(List, []),
|
111
|
+
description="Only check samples based on these status flags.",
|
112
|
+
)
|
113
|
+
|
114
|
+
TIME_DELAY = Argument(
|
115
|
+
name="time_delay",
|
116
|
+
alias="-t",
|
117
|
+
default=(int, 0),
|
118
|
+
description="Time delay in seconds between job submissions (min: 0, max: 30)",
|
119
|
+
)
|
120
|
+
DRY_RUN = Argument(
|
121
|
+
name="dry_run",
|
122
|
+
alias="-d",
|
123
|
+
default=(bool, False),
|
124
|
+
description="Don't actually submit jobs",
|
125
|
+
)
|
126
|
+
COMMAND_EXTRA = Argument(
|
127
|
+
name="command_extra",
|
128
|
+
alias="-x",
|
129
|
+
default=(str, ""),
|
130
|
+
description="String to append to every command",
|
131
|
+
)
|
132
|
+
COMMAND_EXTRA_OVERRIDE = Argument(
|
133
|
+
name="command_extra_override",
|
134
|
+
alias="-y",
|
135
|
+
default=(str, ""),
|
136
|
+
description="Same as command-extra, but overrides values in PEP",
|
137
|
+
)
|
138
|
+
LUMP = Argument(
|
139
|
+
name="lump",
|
140
|
+
alias="-u",
|
141
|
+
default=(float, None),
|
142
|
+
description="Total input file size (GB) to batch into one job",
|
143
|
+
)
|
144
|
+
LUMPN = Argument(
|
145
|
+
name="lump_n",
|
146
|
+
alias="-n",
|
147
|
+
default=(int, None),
|
148
|
+
description="Number of commands to batch into one job",
|
149
|
+
)
|
150
|
+
LUMPJ = Argument(
|
151
|
+
name="lump_j",
|
152
|
+
alias="-j",
|
153
|
+
default=(int, None),
|
154
|
+
description="Lump samples into number of jobs.",
|
155
|
+
)
|
156
|
+
LIMIT = Argument(
|
157
|
+
name="limit", alias="-l", default=(int, None), description="Limit to n samples"
|
158
|
+
)
|
159
|
+
SKIP = Argument(
|
160
|
+
name="skip",
|
161
|
+
alias="-k",
|
162
|
+
default=(int, None),
|
163
|
+
description="Skip samples by numerical index",
|
164
|
+
)
|
165
|
+
CONFIG = Argument(
|
166
|
+
name="config",
|
167
|
+
alias="-c",
|
168
|
+
default=(str, None),
|
169
|
+
description="Looper configuration file (YAML)",
|
170
|
+
)
|
171
|
+
SETTINGS = Argument(
|
172
|
+
name="settings",
|
173
|
+
default=(str, ""),
|
174
|
+
description="Path to a YAML settings file with compute settings",
|
175
|
+
)
|
176
|
+
PEP_CONFIG = Argument(
|
177
|
+
name="pep_config",
|
178
|
+
default=(str, None),
|
179
|
+
description="PEP configuration file",
|
180
|
+
)
|
181
|
+
OUTPUT_DIR = Argument(
|
182
|
+
name="output_dir",
|
183
|
+
alias="-o",
|
184
|
+
default=(str, None),
|
185
|
+
description="Output directory",
|
186
|
+
)
|
187
|
+
REPORT_OUTPUT_DIR = Argument(
|
188
|
+
name="report_dir",
|
189
|
+
alias="-r",
|
190
|
+
default=(str, None),
|
191
|
+
description="Set location for looper report and looper table outputs",
|
192
|
+
)
|
193
|
+
|
194
|
+
GENERIC = Argument(
|
195
|
+
name="generic",
|
196
|
+
alias="-g",
|
197
|
+
default=(bool, False),
|
198
|
+
description="Use generic looper config?",
|
199
|
+
)
|
200
|
+
|
201
|
+
SAMPLE_PIPELINE_INTERFACES = Argument(
|
202
|
+
name="sample_pipeline_interfaces",
|
203
|
+
alias="-S",
|
204
|
+
default=(List, []),
|
205
|
+
description="Paths to looper sample pipeline interfaces",
|
206
|
+
)
|
207
|
+
PROJECT_PIPELINE_INTERFACES = Argument(
|
208
|
+
name="project_pipeline_interfaces",
|
209
|
+
alias="-P",
|
210
|
+
default=(List, []),
|
211
|
+
description="Paths to looper project pipeline interfaces",
|
212
|
+
)
|
213
|
+
AMEND = Argument(
|
214
|
+
name="amend", default=(List, []), description="List of amendments to activate"
|
215
|
+
)
|
216
|
+
SEL_ATTR = Argument(
|
217
|
+
name="sel_attr",
|
218
|
+
default=(str, "toggle"),
|
219
|
+
description="Attribute for sample exclusion OR inclusion",
|
220
|
+
)
|
221
|
+
SEL_INCL = Argument(
|
222
|
+
name="sel_incl",
|
223
|
+
default=(List, []),
|
224
|
+
description="Include only samples with these values",
|
225
|
+
)
|
226
|
+
SEL_EXCL = Argument(
|
227
|
+
name="sel_excl",
|
228
|
+
default=(str, ""),
|
229
|
+
description="Exclude samples with these values",
|
230
|
+
)
|
231
|
+
SEL_FLAG = Argument(
|
232
|
+
name="sel_flag", default=(List, []), description="Sample selection flag"
|
233
|
+
)
|
234
|
+
EXC_FLAG = Argument(
|
235
|
+
name="exc_flag", default=(List, []), description="Sample exclusion flag"
|
236
|
+
)
|
237
|
+
SKIP_FILE_CHECKS = Argument(
|
238
|
+
name="skip_file_checks",
|
239
|
+
alias="-f",
|
240
|
+
default=(bool, False),
|
241
|
+
description="Do not perform input file checks",
|
242
|
+
)
|
243
|
+
PACKAGE = Argument(
|
244
|
+
name="package",
|
245
|
+
alias="-p",
|
246
|
+
default=(str, None),
|
247
|
+
description="Name of computing resource package to use",
|
248
|
+
)
|
249
|
+
COMPUTE = Argument(
|
250
|
+
name="compute",
|
251
|
+
default=(List, []),
|
252
|
+
description="List of key-value pairs (k1=v1)",
|
253
|
+
)
|
254
|
+
DIVVY = Argument(
|
255
|
+
name="divvy",
|
256
|
+
default=(str, os.getenv("DIVCFG", None)),
|
257
|
+
description=(
|
258
|
+
"Path to divvy configuration file. Default=$DIVCFG env "
|
259
|
+
"variable. Currently: {}".format(os.getenv("DIVCFG") or "not set")
|
260
|
+
),
|
261
|
+
)
|
262
|
+
# Arguments for logger compatible with logmuse
|
263
|
+
SILENT = Argument(
|
264
|
+
name="silent", default=(bool, False), description="Whether to silence logging"
|
265
|
+
)
|
266
|
+
VERBOSITY = Argument(
|
267
|
+
name="verbosity",
|
268
|
+
default=(int, None),
|
269
|
+
description="Alternate mode of expression for logging level that better "
|
270
|
+
"accords with intuition about how to convey this.",
|
271
|
+
)
|
272
|
+
LOGDEV = Argument(
|
273
|
+
name="logdev",
|
274
|
+
default=(bool, False),
|
275
|
+
description="Whether to log in development mode; possibly among other "
|
276
|
+
"behavioral changes to logs handling, use a more information-rich "
|
277
|
+
"message format template.",
|
278
|
+
)
|
279
|
+
PIPESTAT = Argument(
|
280
|
+
name="pipestat",
|
281
|
+
default=(str, None),
|
282
|
+
description="Path to pipestat files.",
|
283
|
+
)
|
284
|
+
PORTABLE = Argument(
|
285
|
+
name="portable",
|
286
|
+
default=(bool, False),
|
287
|
+
description="Makes html report portable.",
|
288
|
+
)
|
289
|
+
PROJECT_LEVEL = Argument(
|
290
|
+
name="project",
|
291
|
+
default=(bool, False),
|
292
|
+
description="Is this command executed for project-level?",
|
293
|
+
)
|
@@ -0,0 +1,335 @@
|
|
1
|
+
"""
|
2
|
+
`pydantic` models for `looper` commands and a wrapper class.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from typing import List, Optional, Type, Union
|
7
|
+
|
8
|
+
import pydantic.v1 as pydantic
|
9
|
+
|
10
|
+
from ..const import MESSAGE_BY_SUBCOMMAND
|
11
|
+
from .arguments import Argument, ArgumentEnum
|
12
|
+
from pydantic_argparse import ArgumentParser
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class Command:
|
17
|
+
"""
|
18
|
+
Representation of a command
|
19
|
+
|
20
|
+
:param str name: command name
|
21
|
+
:param str description: command description
|
22
|
+
:param list[Argument] arguments: list of arguments supported by this command
|
23
|
+
"""
|
24
|
+
|
25
|
+
name: str
|
26
|
+
description: str
|
27
|
+
arguments: List[Argument]
|
28
|
+
|
29
|
+
def create_model(self) -> Type[pydantic.BaseModel]:
|
30
|
+
"""
|
31
|
+
Creates a `pydantic` model for this command
|
32
|
+
"""
|
33
|
+
arguments = dict()
|
34
|
+
for arg in self.arguments:
|
35
|
+
# These gymnastics are necessary because of
|
36
|
+
# https://github.com/pydantic/pydantic/issues/2248#issuecomment-757448447
|
37
|
+
arg_type, arg_default_value = arg.default
|
38
|
+
arguments[arg.name] = (
|
39
|
+
arg_type,
|
40
|
+
pydantic.Field(arg_default_value, description=arg.description),
|
41
|
+
)
|
42
|
+
return pydantic.create_model(self.name, **arguments)
|
43
|
+
|
44
|
+
|
45
|
+
SHARED_ARGUMENTS = [
|
46
|
+
ArgumentEnum.SETTINGS.value,
|
47
|
+
ArgumentEnum.EXC_FLAG.value,
|
48
|
+
ArgumentEnum.SEL_FLAG.value,
|
49
|
+
ArgumentEnum.SEL_ATTR.value,
|
50
|
+
ArgumentEnum.SEL_INCL.value,
|
51
|
+
ArgumentEnum.SEL_EXCL.value,
|
52
|
+
ArgumentEnum.LIMIT.value,
|
53
|
+
ArgumentEnum.SKIP.value,
|
54
|
+
ArgumentEnum.PEP_CONFIG.value,
|
55
|
+
ArgumentEnum.OUTPUT_DIR.value,
|
56
|
+
ArgumentEnum.CONFIG.value,
|
57
|
+
ArgumentEnum.SAMPLE_PIPELINE_INTERFACES.value,
|
58
|
+
ArgumentEnum.PROJECT_PIPELINE_INTERFACES.value,
|
59
|
+
ArgumentEnum.PIPESTAT.value,
|
60
|
+
ArgumentEnum.SETTINGS.value,
|
61
|
+
ArgumentEnum.AMEND.value,
|
62
|
+
ArgumentEnum.PROJECT_LEVEL.value,
|
63
|
+
]
|
64
|
+
|
65
|
+
RunParser = Command(
|
66
|
+
"run",
|
67
|
+
MESSAGE_BY_SUBCOMMAND["run"],
|
68
|
+
[
|
69
|
+
ArgumentEnum.IGNORE_FLAGS.value,
|
70
|
+
ArgumentEnum.TIME_DELAY.value,
|
71
|
+
ArgumentEnum.DRY_RUN.value,
|
72
|
+
ArgumentEnum.COMMAND_EXTRA.value,
|
73
|
+
ArgumentEnum.COMMAND_EXTRA_OVERRIDE.value,
|
74
|
+
ArgumentEnum.LUMP.value,
|
75
|
+
ArgumentEnum.LUMPN.value,
|
76
|
+
ArgumentEnum.LUMPJ.value,
|
77
|
+
ArgumentEnum.DIVVY.value,
|
78
|
+
ArgumentEnum.SKIP_FILE_CHECKS.value,
|
79
|
+
ArgumentEnum.COMPUTE.value,
|
80
|
+
ArgumentEnum.PACKAGE.value,
|
81
|
+
],
|
82
|
+
)
|
83
|
+
|
84
|
+
# RERUN
|
85
|
+
RerunParser = Command(
|
86
|
+
"rerun",
|
87
|
+
MESSAGE_BY_SUBCOMMAND["rerun"],
|
88
|
+
[
|
89
|
+
ArgumentEnum.IGNORE_FLAGS.value,
|
90
|
+
ArgumentEnum.TIME_DELAY.value,
|
91
|
+
ArgumentEnum.DRY_RUN.value,
|
92
|
+
ArgumentEnum.COMMAND_EXTRA.value,
|
93
|
+
ArgumentEnum.COMMAND_EXTRA_OVERRIDE.value,
|
94
|
+
ArgumentEnum.LUMP.value,
|
95
|
+
ArgumentEnum.LUMPN.value,
|
96
|
+
ArgumentEnum.LUMPJ.value,
|
97
|
+
ArgumentEnum.DIVVY.value,
|
98
|
+
ArgumentEnum.SKIP_FILE_CHECKS.value,
|
99
|
+
ArgumentEnum.COMPUTE.value,
|
100
|
+
ArgumentEnum.PACKAGE.value,
|
101
|
+
],
|
102
|
+
)
|
103
|
+
|
104
|
+
# RUNP
|
105
|
+
RunProjectParser = Command(
|
106
|
+
"runp",
|
107
|
+
MESSAGE_BY_SUBCOMMAND["runp"],
|
108
|
+
[
|
109
|
+
ArgumentEnum.IGNORE_FLAGS.value,
|
110
|
+
ArgumentEnum.TIME_DELAY.value,
|
111
|
+
ArgumentEnum.DRY_RUN.value,
|
112
|
+
ArgumentEnum.COMMAND_EXTRA.value,
|
113
|
+
ArgumentEnum.COMMAND_EXTRA_OVERRIDE.value,
|
114
|
+
ArgumentEnum.LUMP.value,
|
115
|
+
ArgumentEnum.LUMPN.value,
|
116
|
+
ArgumentEnum.DIVVY.value,
|
117
|
+
ArgumentEnum.SKIP_FILE_CHECKS.value,
|
118
|
+
ArgumentEnum.COMPUTE.value,
|
119
|
+
ArgumentEnum.PACKAGE.value,
|
120
|
+
],
|
121
|
+
)
|
122
|
+
|
123
|
+
# TABLE
|
124
|
+
TableParser = Command(
|
125
|
+
"table",
|
126
|
+
MESSAGE_BY_SUBCOMMAND["table"],
|
127
|
+
[
|
128
|
+
ArgumentEnum.REPORT_OUTPUT_DIR.value,
|
129
|
+
],
|
130
|
+
)
|
131
|
+
|
132
|
+
|
133
|
+
# REPORT
|
134
|
+
ReportParser = Command(
|
135
|
+
"report",
|
136
|
+
MESSAGE_BY_SUBCOMMAND["report"],
|
137
|
+
[
|
138
|
+
ArgumentEnum.PORTABLE.value,
|
139
|
+
ArgumentEnum.REPORT_OUTPUT_DIR.value,
|
140
|
+
],
|
141
|
+
)
|
142
|
+
|
143
|
+
# DESTROY
|
144
|
+
DestroyParser = Command(
|
145
|
+
"destroy",
|
146
|
+
MESSAGE_BY_SUBCOMMAND["destroy"],
|
147
|
+
[
|
148
|
+
ArgumentEnum.DRY_RUN.value,
|
149
|
+
ArgumentEnum.FORCE_YES.value,
|
150
|
+
],
|
151
|
+
)
|
152
|
+
|
153
|
+
# CHECK
|
154
|
+
CheckParser = Command(
|
155
|
+
"check",
|
156
|
+
MESSAGE_BY_SUBCOMMAND["check"],
|
157
|
+
[
|
158
|
+
ArgumentEnum.DESCRIBE_CODES.value,
|
159
|
+
ArgumentEnum.ITEMIZED.value,
|
160
|
+
ArgumentEnum.FLAGS.value,
|
161
|
+
],
|
162
|
+
)
|
163
|
+
|
164
|
+
# CLEAN
|
165
|
+
CleanParser = Command(
|
166
|
+
"clean",
|
167
|
+
MESSAGE_BY_SUBCOMMAND["clean"],
|
168
|
+
[
|
169
|
+
ArgumentEnum.DRY_RUN.value,
|
170
|
+
ArgumentEnum.FORCE_YES.value,
|
171
|
+
],
|
172
|
+
)
|
173
|
+
|
174
|
+
# INSPECT
|
175
|
+
InspectParser = Command(
|
176
|
+
"inspect",
|
177
|
+
MESSAGE_BY_SUBCOMMAND["inspect"],
|
178
|
+
[],
|
179
|
+
)
|
180
|
+
|
181
|
+
|
182
|
+
# INIT
|
183
|
+
InitParser = Command(
|
184
|
+
"init",
|
185
|
+
MESSAGE_BY_SUBCOMMAND["init"],
|
186
|
+
[
|
187
|
+
# Original command has force flag which is technically a different flag, but we should just use FORCE_YES
|
188
|
+
ArgumentEnum.FORCE_YES.value,
|
189
|
+
ArgumentEnum.OUTPUT_DIR.value,
|
190
|
+
ArgumentEnum.PEP_CONFIG.value,
|
191
|
+
ArgumentEnum.SAMPLE_PIPELINE_INTERFACES.value,
|
192
|
+
ArgumentEnum.PROJECT_PIPELINE_INTERFACES.value,
|
193
|
+
ArgumentEnum.GENERIC.value,
|
194
|
+
],
|
195
|
+
)
|
196
|
+
|
197
|
+
|
198
|
+
# INIT-PIFACE
|
199
|
+
InitPifaceParser = Command(
|
200
|
+
"init_piface",
|
201
|
+
MESSAGE_BY_SUBCOMMAND["init-piface"],
|
202
|
+
[],
|
203
|
+
)
|
204
|
+
|
205
|
+
|
206
|
+
# LINK
|
207
|
+
LinkParser = Command(
|
208
|
+
"link",
|
209
|
+
MESSAGE_BY_SUBCOMMAND["link"],
|
210
|
+
[],
|
211
|
+
)
|
212
|
+
|
213
|
+
|
214
|
+
# Add shared arguments for all commands that use them
|
215
|
+
for arg in SHARED_ARGUMENTS:
|
216
|
+
RunParser.arguments.append(arg)
|
217
|
+
RerunParser.arguments.append(arg)
|
218
|
+
RunProjectParser.arguments.append(arg)
|
219
|
+
ReportParser.arguments.append(arg)
|
220
|
+
DestroyParser.arguments.append(arg)
|
221
|
+
CheckParser.arguments.append(arg)
|
222
|
+
CleanParser.arguments.append(arg)
|
223
|
+
TableParser.arguments.append(arg)
|
224
|
+
LinkParser.arguments.append(arg)
|
225
|
+
InspectParser.arguments.append(arg)
|
226
|
+
|
227
|
+
# Create all Models
|
228
|
+
RunParserModel = RunParser.create_model()
|
229
|
+
RerunParserModel = RerunParser.create_model()
|
230
|
+
RunProjectParserModel = RunProjectParser.create_model()
|
231
|
+
ReportParserModel = ReportParser.create_model()
|
232
|
+
DestroyParserModel = DestroyParser.create_model()
|
233
|
+
CheckParserModel = CheckParser.create_model()
|
234
|
+
CleanParserModel = CleanParser.create_model()
|
235
|
+
TableParserModel = TableParser.create_model()
|
236
|
+
LinkParserModel = LinkParser.create_model()
|
237
|
+
InspectParserModel = InspectParser.create_model()
|
238
|
+
InitParserModel = InitParser.create_model()
|
239
|
+
InitPifaceParserModel = InitPifaceParser.create_model()
|
240
|
+
|
241
|
+
|
242
|
+
def add_short_arguments(
|
243
|
+
parser: ArgumentParser, argument_enums: Type[ArgumentEnum]
|
244
|
+
) -> ArgumentParser:
|
245
|
+
"""
|
246
|
+
This function takes a parser object created under pydantic argparse and adds the short arguments AFTER the initial creation.
|
247
|
+
This is a workaround as pydantic-argparse does not currently support this during initial parser creation.
|
248
|
+
|
249
|
+
:param ArgumentParser parser: parser before adding short arguments
|
250
|
+
:param Type[ArgumentEnum] argument_enums: enumeration of arguments that contain names and aliases
|
251
|
+
:return ArgumentParser parser: parser after short arguments have been added
|
252
|
+
"""
|
253
|
+
|
254
|
+
for cmd in parser._subcommands.choices.keys():
|
255
|
+
|
256
|
+
for argument_enum in list(argument_enums):
|
257
|
+
# First check there is an alias for the argument otherwise skip
|
258
|
+
if argument_enum.value.alias:
|
259
|
+
short_key = argument_enum.value.alias
|
260
|
+
long_key = "--" + argument_enum.value.name.replace(
|
261
|
+
"_", "-"
|
262
|
+
) # We must do this because the ArgumentEnum names are transformed during parser creation
|
263
|
+
if long_key in parser._subcommands.choices[cmd]._option_string_actions:
|
264
|
+
argument = parser._subcommands.choices[cmd]._option_string_actions[
|
265
|
+
long_key
|
266
|
+
]
|
267
|
+
argument.option_strings = (short_key, long_key)
|
268
|
+
parser._subcommands.choices[cmd]._option_string_actions[
|
269
|
+
short_key
|
270
|
+
] = argument
|
271
|
+
|
272
|
+
return parser
|
273
|
+
|
274
|
+
|
275
|
+
SUPPORTED_COMMANDS = [
|
276
|
+
RunParser,
|
277
|
+
RerunParser,
|
278
|
+
RunProjectParser,
|
279
|
+
TableParser,
|
280
|
+
ReportParser,
|
281
|
+
DestroyParser,
|
282
|
+
CheckParser,
|
283
|
+
CleanParser,
|
284
|
+
InitParser,
|
285
|
+
InitPifaceParser,
|
286
|
+
LinkParser,
|
287
|
+
InspectParser,
|
288
|
+
]
|
289
|
+
|
290
|
+
|
291
|
+
class TopLevelParser(pydantic.BaseModel):
|
292
|
+
"""
|
293
|
+
Top level parser that takes
|
294
|
+
- commands (run, runp, check...)
|
295
|
+
- arguments that are required no matter the subcommand
|
296
|
+
"""
|
297
|
+
|
298
|
+
# commands
|
299
|
+
run: Optional[RunParserModel] = pydantic.Field(description=RunParser.description)
|
300
|
+
rerun: Optional[RerunParserModel] = pydantic.Field(
|
301
|
+
description=RerunParser.description
|
302
|
+
)
|
303
|
+
runp: Optional[RunProjectParserModel] = pydantic.Field(
|
304
|
+
description=RunProjectParser.description
|
305
|
+
)
|
306
|
+
table: Optional[TableParserModel] = pydantic.Field(
|
307
|
+
description=TableParser.description
|
308
|
+
)
|
309
|
+
report: Optional[ReportParserModel] = pydantic.Field(
|
310
|
+
description=ReportParser.description
|
311
|
+
)
|
312
|
+
destroy: Optional[DestroyParserModel] = pydantic.Field(
|
313
|
+
description=DestroyParser.description
|
314
|
+
)
|
315
|
+
check: Optional[CheckParserModel] = pydantic.Field(
|
316
|
+
description=CheckParser.description
|
317
|
+
)
|
318
|
+
clean: Optional[CleanParserModel] = pydantic.Field(
|
319
|
+
description=CleanParser.description
|
320
|
+
)
|
321
|
+
init: Optional[InitParserModel] = pydantic.Field(description=InitParser.description)
|
322
|
+
init_piface: Optional[InitPifaceParserModel] = pydantic.Field(
|
323
|
+
description=InitPifaceParser.description
|
324
|
+
)
|
325
|
+
link: Optional[LinkParserModel] = pydantic.Field(description=LinkParser.description)
|
326
|
+
|
327
|
+
inspect: Optional[InspectParserModel] = pydantic.Field(
|
328
|
+
description=InspectParser.description
|
329
|
+
)
|
330
|
+
|
331
|
+
# Additional arguments for logging, added to ALL commands
|
332
|
+
# These must be used before the command
|
333
|
+
silent: Optional[bool] = ArgumentEnum.SILENT.value.with_reduced_default()
|
334
|
+
verbosity: Optional[int] = ArgumentEnum.VERBOSITY.value.with_reduced_default()
|
335
|
+
logdev: Optional[bool] = ArgumentEnum.LOGDEV.value.with_reduced_default()
|