looper 1.7.0__py3-none-any.whl → 2.0.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.
- 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 +147 -28
- looper/const.py +9 -0
- looper/divvy.py +56 -47
- looper/exceptions.py +9 -1
- looper/looper.py +196 -169
- 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.0.dist-info → looper-2.0.0.dist-info}/METADATA +24 -14
- {looper-1.7.0.dist-info → looper-2.0.0.dist-info}/RECORD +24 -19
- {looper-1.7.0.dist-info → looper-2.0.0.dist-info}/WHEEL +1 -1
- {looper-1.7.0.dist-info → looper-2.0.0.dist-info}/entry_points.txt +1 -1
- looper/cli_looper.py +0 -796
- {looper-1.7.0.dist-info → looper-2.0.0.dist-info}/LICENSE.txt +0 -0
- {looper-1.7.0.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()
|