toil 7.0.0__py3-none-any.whl → 8.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.
- toil/__init__.py +121 -83
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +137 -77
- toil/batchSystems/abstractGridEngineBatchSystem.py +211 -101
- toil/batchSystems/awsBatch.py +237 -128
- toil/batchSystems/cleanup_support.py +22 -16
- toil/batchSystems/contained_executor.py +30 -26
- toil/batchSystems/gridengine.py +85 -49
- toil/batchSystems/htcondor.py +164 -87
- toil/batchSystems/kubernetes.py +622 -386
- toil/batchSystems/local_support.py +17 -12
- toil/batchSystems/lsf.py +132 -79
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +288 -149
- toil/batchSystems/mesos/executor.py +77 -49
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +38 -29
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +293 -123
- toil/batchSystems/slurm.py +489 -137
- toil/batchSystems/torque.py +46 -32
- toil/bus.py +141 -73
- toil/common.py +630 -359
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1114 -532
- toil/cwl/utils.py +17 -22
- toil/deferred.py +62 -41
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +88 -57
- toil/fileStores/cachingFileStore.py +711 -247
- toil/fileStores/nonCachingFileStore.py +113 -75
- toil/job.py +988 -315
- toil/jobStores/abstractJobStore.py +387 -243
- toil/jobStores/aws/jobStore.py +727 -403
- toil/jobStores/aws/utils.py +161 -109
- toil/jobStores/conftest.py +1 -0
- toil/jobStores/fileJobStore.py +289 -151
- toil/jobStores/googleJobStore.py +137 -70
- toil/jobStores/utils.py +36 -15
- toil/leader.py +614 -269
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +55 -28
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +193 -58
- toil/lib/aws/utils.py +238 -218
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +83 -49
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +322 -209
- toil/lib/ec2nodes.py +174 -106
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +4 -2
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +99 -11
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +65 -18
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +19 -7
- toil/lib/retry.py +115 -77
- toil/lib/threading.py +282 -80
- toil/lib/throttle.py +15 -14
- toil/options/common.py +834 -401
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +70 -19
- toil/provisioners/__init__.py +111 -46
- toil/provisioners/abstractProvisioner.py +322 -157
- toil/provisioners/aws/__init__.py +62 -30
- toil/provisioners/aws/awsProvisioner.py +980 -627
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +147 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +127 -61
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +148 -64
- toil/test/__init__.py +263 -179
- toil/test/batchSystems/batchSystemTest.py +438 -195
- toil/test/batchSystems/batch_system_plugin_test.py +18 -7
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +93 -47
- toil/test/cactus/test_cactus_integration.py +20 -22
- toil/test/cwl/cwlTest.py +271 -71
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/docs/scriptsTest.py +60 -34
- toil/test/jobStores/jobStoreTest.py +412 -235
- toil/test/lib/aws/test_iam.py +116 -48
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +57 -49
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/options.py +7 -2
- toil/test/provisioners/aws/awsProvisionerTest.py +293 -140
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +81 -42
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +140 -100
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +33 -26
- toil/test/src/environmentTest.py +20 -10
- toil/test/src/fileStoreTest.py +538 -271
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +32 -17
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +120 -70
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +6 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +33 -16
- toil/test/utils/toilDebugTest.py +70 -58
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +239 -102
- toil/test/wdl/wdltoil_test.py +789 -148
- toil/test/wdl/wdltoil_test_kubernetes.py +37 -23
- toil/toilState.py +52 -26
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +85 -25
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +251 -145
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +27 -14
- toil/utils/toilSshCluster.py +45 -22
- toil/utils/toilStats.py +75 -36
- toil/utils/toilStatus.py +226 -119
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +11 -11
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3513 -1052
- toil/worker.py +269 -128
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-7.0.0.dist-info/METADATA +0 -158
- toil-7.0.0.dist-info/RECORD +0 -244
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/LICENSE +0 -0
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-7.0.0.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
toil/utils/toilSshCluster.py
CHANGED
|
@@ -15,48 +15,71 @@
|
|
|
15
15
|
import argparse
|
|
16
16
|
import logging
|
|
17
17
|
import sys
|
|
18
|
-
from typing import List
|
|
19
18
|
|
|
20
19
|
from toil.common import parser_with_common_options
|
|
21
|
-
from toil.provisioners import
|
|
20
|
+
from toil.provisioners import NoSuchClusterException, cluster_factory
|
|
22
21
|
from toil.statsAndLogging import set_logging_from_options
|
|
23
22
|
|
|
24
23
|
logger = logging.getLogger(__name__)
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
def main() -> None:
|
|
28
|
-
parser = parser_with_common_options(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
parser.add_argument(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
parser = parser_with_common_options(
|
|
28
|
+
provisioner_options=True, jobstore_option=False, prog="toil ssh-cluster"
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--insecure",
|
|
32
|
+
action="store_true",
|
|
33
|
+
help="Temporarily disable strict host key checking.",
|
|
34
|
+
)
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"--sshOption",
|
|
37
|
+
dest="sshOptions",
|
|
38
|
+
default=[],
|
|
39
|
+
action="append",
|
|
40
|
+
help="Pass an additional option to the SSH command.",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--grafana_port",
|
|
44
|
+
dest="grafana_port",
|
|
45
|
+
default=3000,
|
|
46
|
+
help="Assign a local port to be used for the Grafana dashboard.",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument("args", nargs=argparse.REMAINDER)
|
|
36
49
|
options = parser.parse_args()
|
|
37
50
|
set_logging_from_options(options)
|
|
38
51
|
|
|
39
52
|
# Since we collect all the remaining arguments at the end for a command to
|
|
40
53
|
# run, it's easy to lose options.
|
|
41
|
-
if len(options.args) > 0 and options.args[0].startswith(
|
|
42
|
-
logger.warning(
|
|
43
|
-
|
|
54
|
+
if len(options.args) > 0 and options.args[0].startswith("-"):
|
|
55
|
+
logger.warning(
|
|
56
|
+
"Argument '%s' interpreted as a command to run "
|
|
57
|
+
"despite looking like an option.",
|
|
58
|
+
options.args[0],
|
|
59
|
+
)
|
|
44
60
|
|
|
45
|
-
cluster = cluster_factory(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
cluster = cluster_factory(
|
|
62
|
+
provisioner=options.provisioner,
|
|
63
|
+
clusterName=options.clusterName,
|
|
64
|
+
zone=options.zone,
|
|
65
|
+
)
|
|
66
|
+
command = options.args if options.args else ["bash"]
|
|
67
|
+
sshOptions: list[str] = options.sshOptions
|
|
50
68
|
|
|
51
69
|
# Forward ports:
|
|
52
70
|
# 3000 for Grafana dashboard
|
|
53
71
|
# 9090 for Prometheus dashboard
|
|
54
|
-
sshOptions.extend(
|
|
55
|
-
|
|
72
|
+
sshOptions.extend(
|
|
73
|
+
["-L", f"{options.grafana_port}:localhost:3000", "-L", "9090:localhost:9090"]
|
|
74
|
+
)
|
|
56
75
|
|
|
57
76
|
try:
|
|
58
|
-
cluster.getLeader().sshAppliance(
|
|
59
|
-
|
|
77
|
+
cluster.getLeader().sshAppliance(
|
|
78
|
+
*command,
|
|
79
|
+
strict=not options.insecure,
|
|
80
|
+
tty=sys.stdin.isatty(),
|
|
81
|
+
sshOptions=sshOptions,
|
|
82
|
+
)
|
|
60
83
|
except NoSuchClusterException as e:
|
|
61
84
|
logger.error(e)
|
|
62
85
|
sys.exit(1)
|
toil/utils/toilStats.py
CHANGED
|
@@ -18,7 +18,7 @@ import math
|
|
|
18
18
|
import sys
|
|
19
19
|
from argparse import ArgumentParser, Namespace
|
|
20
20
|
from functools import partial
|
|
21
|
-
from typing import Any, Callable,
|
|
21
|
+
from typing import Any, Callable, Optional, TextIO, Union
|
|
22
22
|
|
|
23
23
|
from toil.common import Config, Toil, parser_with_common_options
|
|
24
24
|
from toil.job import Job
|
|
@@ -37,7 +37,7 @@ CATEGORY_UNITS = {
|
|
|
37
37
|
"clock": "core-s",
|
|
38
38
|
"wait": "core-s",
|
|
39
39
|
"memory": "KiB",
|
|
40
|
-
"disk": "B"
|
|
40
|
+
"disk": "B",
|
|
41
41
|
}
|
|
42
42
|
# These are what we call them to the user
|
|
43
43
|
TITLES = {
|
|
@@ -45,7 +45,7 @@ TITLES = {
|
|
|
45
45
|
"clock": "CPU Time",
|
|
46
46
|
"wait": "CPU Wait",
|
|
47
47
|
"memory": "Memory",
|
|
48
|
-
"disk": "Disk"
|
|
48
|
+
"disk": "Disk",
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
# Of those, these are in time
|
|
@@ -65,6 +65,7 @@ LONG_FORMS = {
|
|
|
65
65
|
"max": "max",
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
|
|
68
69
|
class ColumnWidths:
|
|
69
70
|
"""
|
|
70
71
|
Convenience object that stores the width of columns for printing. Helps make things pretty.
|
|
@@ -74,7 +75,7 @@ class ColumnWidths:
|
|
|
74
75
|
self.categories = CATEGORIES
|
|
75
76
|
self.fields_count = ["count", "min", "med", "ave", "max", "total"]
|
|
76
77
|
self.fields = ["min", "med", "ave", "max", "total"]
|
|
77
|
-
self.data:
|
|
78
|
+
self.data: dict[str, int] = {}
|
|
78
79
|
for category in self.categories:
|
|
79
80
|
for field in self.fields_count:
|
|
80
81
|
self.set_width(category, field, 8)
|
|
@@ -110,21 +111,27 @@ def pretty_space(k: float, field: Optional[int] = None, alone: bool = False) ->
|
|
|
110
111
|
# If we don't have a header to say bytes, include the B.
|
|
111
112
|
trailer = "B" if alone else ""
|
|
112
113
|
if k < 1024:
|
|
113
|
-
return pad_str("
|
|
114
|
+
return pad_str("{:g}Ki{}".format(k, trailer), field)
|
|
114
115
|
if k < (1024 * 1024):
|
|
115
|
-
return pad_str("
|
|
116
|
+
return pad_str("{:.1f}Mi{}".format(k / 1024.0, trailer), field)
|
|
116
117
|
if k < (1024 * 1024 * 1024):
|
|
117
|
-
return pad_str("
|
|
118
|
+
return pad_str("{:.1f}Gi{}".format(k / 1024.0 / 1024.0, trailer), field)
|
|
118
119
|
if k < (1024 * 1024 * 1024 * 1024):
|
|
119
|
-
return pad_str(
|
|
120
|
+
return pad_str(
|
|
121
|
+
"{:.1f}Ti{}".format(k / 1024.0 / 1024.0 / 1024.0, trailer), field
|
|
122
|
+
)
|
|
120
123
|
if k < (1024 * 1024 * 1024 * 1024 * 1024):
|
|
121
|
-
return pad_str(
|
|
124
|
+
return pad_str(
|
|
125
|
+
"{:.1f}Pi{}".format(k / 1024.0 / 1024.0 / 1024.0 / 1024.0, trailer), field
|
|
126
|
+
)
|
|
122
127
|
|
|
123
128
|
# due to https://stackoverflow.com/questions/47149154
|
|
124
129
|
assert False
|
|
125
130
|
|
|
126
131
|
|
|
127
|
-
def pretty_time(
|
|
132
|
+
def pretty_time(
|
|
133
|
+
t: float, field: Optional[int] = None, unit: str = "s", alone: bool = False
|
|
134
|
+
) -> str:
|
|
128
135
|
"""
|
|
129
136
|
Given input t as seconds, return a nicely formatted string.
|
|
130
137
|
"""
|
|
@@ -170,7 +177,10 @@ def pretty_time(t: float, field: Optional[int] = None, unit: str = "s", alone: b
|
|
|
170
177
|
s = t % 60
|
|
171
178
|
wPlural = pluralDict[w > 1]
|
|
172
179
|
dPlural = pluralDict[d > 1]
|
|
173
|
-
return pad_str(
|
|
180
|
+
return pad_str(
|
|
181
|
+
"%dweek%s%dday%s%dh%dm%d%s" % (w, wPlural, d, dPlural, h, m, s, unit_str), field
|
|
182
|
+
)
|
|
183
|
+
|
|
174
184
|
|
|
175
185
|
def report_unit(unit: str) -> str:
|
|
176
186
|
"""
|
|
@@ -180,7 +190,14 @@ def report_unit(unit: str) -> str:
|
|
|
180
190
|
return "core·s"
|
|
181
191
|
return unit
|
|
182
192
|
|
|
183
|
-
|
|
193
|
+
|
|
194
|
+
def report_time(
|
|
195
|
+
t: float,
|
|
196
|
+
options: Namespace,
|
|
197
|
+
field: Optional[int] = None,
|
|
198
|
+
unit: str = "s",
|
|
199
|
+
alone: bool = False,
|
|
200
|
+
) -> str:
|
|
184
201
|
"""Given t seconds, report back the correct format as string."""
|
|
185
202
|
assert unit in ("s", "core-s")
|
|
186
203
|
if options.pretty:
|
|
@@ -189,11 +206,15 @@ def report_time(t: float, options: Namespace, field: Optional[int] = None, unit:
|
|
|
189
206
|
if field is not None:
|
|
190
207
|
assert field >= len(unit_text)
|
|
191
208
|
return "%*.2f%s" % (field - len(unit_text), t, unit_text)
|
|
192
|
-
return "
|
|
209
|
+
return "{:.2f}{}".format(t, unit_text)
|
|
193
210
|
|
|
194
211
|
|
|
195
212
|
def report_space(
|
|
196
|
-
|
|
213
|
+
k: float,
|
|
214
|
+
options: Namespace,
|
|
215
|
+
field: Optional[int] = None,
|
|
216
|
+
unit: str = "KiB",
|
|
217
|
+
alone: bool = False,
|
|
197
218
|
) -> str:
|
|
198
219
|
"""
|
|
199
220
|
Given k kibibytes, report back the correct format as string.
|
|
@@ -216,10 +237,12 @@ def report_space(
|
|
|
216
237
|
return "%d%s" % (int(k), trailer)
|
|
217
238
|
|
|
218
239
|
|
|
219
|
-
def report_number(
|
|
240
|
+
def report_number(
|
|
241
|
+
n: Union[int, float, None], field: Optional[int] = None, nan_value: str = "NaN"
|
|
242
|
+
) -> str:
|
|
220
243
|
"""
|
|
221
244
|
Given a number, report back the correct format as string.
|
|
222
|
-
|
|
245
|
+
|
|
223
246
|
If it is a NaN or None, use nan_value to represent it instead.
|
|
224
247
|
"""
|
|
225
248
|
if n is None or math.isnan(n):
|
|
@@ -229,7 +252,14 @@ def report_number(n: Union[int, float, None], field: Optional[int] = None, nan_v
|
|
|
229
252
|
# leave room for . and the spacing to the previous field.
|
|
230
253
|
return "%*.*g" % (field, field - 2, n) if field else "%g" % n
|
|
231
254
|
|
|
232
|
-
|
|
255
|
+
|
|
256
|
+
def report(
|
|
257
|
+
v: float,
|
|
258
|
+
category: str,
|
|
259
|
+
options: Namespace,
|
|
260
|
+
field: Optional[int] = None,
|
|
261
|
+
alone=False,
|
|
262
|
+
) -> str:
|
|
233
263
|
"""
|
|
234
264
|
Report a value of the given category formatted as a string.
|
|
235
265
|
|
|
@@ -248,6 +278,7 @@ def report(v: float, category: str, options: Namespace, field: Optional[int] = N
|
|
|
248
278
|
else:
|
|
249
279
|
raise ValueError(f"Unimplemented unit {unit} for category {category}")
|
|
250
280
|
|
|
281
|
+
|
|
251
282
|
def sprint_tag(
|
|
252
283
|
key: str,
|
|
253
284
|
tag: Expando,
|
|
@@ -365,15 +396,15 @@ def get(tree: Expando, name: str) -> float:
|
|
|
365
396
|
return float("nan")
|
|
366
397
|
|
|
367
398
|
|
|
368
|
-
def sort_jobs(jobTypes:
|
|
399
|
+
def sort_jobs(jobTypes: list[Any], options: Namespace) -> list[Any]:
|
|
369
400
|
"""Return a jobTypes all sorted."""
|
|
370
401
|
sortField = LONG_FORMS[options.sortField]
|
|
371
|
-
if
|
|
372
|
-
options.sortCategory in CATEGORIES
|
|
373
|
-
):
|
|
402
|
+
if options.sortCategory in CATEGORIES:
|
|
374
403
|
return sorted(
|
|
375
404
|
jobTypes,
|
|
376
|
-
key=lambda tag: getattr(
|
|
405
|
+
key=lambda tag: getattr(
|
|
406
|
+
tag, "{}_{}".format(sortField, options.sortCategory)
|
|
407
|
+
),
|
|
377
408
|
reverse=options.sort == "decending",
|
|
378
409
|
)
|
|
379
410
|
elif options.sortCategory == "alpha":
|
|
@@ -397,7 +428,7 @@ def report_pretty_data(
|
|
|
397
428
|
root: Expando,
|
|
398
429
|
worker: Expando,
|
|
399
430
|
job: Expando,
|
|
400
|
-
job_types:
|
|
431
|
+
job_types: list[Any],
|
|
401
432
|
options: Namespace,
|
|
402
433
|
) -> str:
|
|
403
434
|
"""Print the important bits out."""
|
|
@@ -426,7 +457,7 @@ def report_pretty_data(
|
|
|
426
457
|
|
|
427
458
|
|
|
428
459
|
def compute_column_widths(
|
|
429
|
-
job_types:
|
|
460
|
+
job_types: list[Any], worker: Expando, job: Expando, options: Namespace
|
|
430
461
|
) -> ColumnWidths:
|
|
431
462
|
"""Return a ColumnWidths() object with the correct max widths."""
|
|
432
463
|
cw = ColumnWidths()
|
|
@@ -451,12 +482,14 @@ def update_column_widths(tag: Expando, cw: ColumnWidths, options: Namespace) ->
|
|
|
451
482
|
cw.set_width(category, field, len(s) + 1)
|
|
452
483
|
|
|
453
484
|
|
|
454
|
-
def build_element(
|
|
485
|
+
def build_element(
|
|
486
|
+
element: Expando, items: list[Job], item_name: str, defaults: dict[str, float]
|
|
487
|
+
) -> Expando:
|
|
455
488
|
"""Create an element for output."""
|
|
456
489
|
|
|
457
490
|
def assertNonnegative(i: float, name: str) -> float:
|
|
458
491
|
if i < 0:
|
|
459
|
-
raise RuntimeError("Negative value
|
|
492
|
+
raise RuntimeError("Negative value {} reported for {}".format(i, name))
|
|
460
493
|
else:
|
|
461
494
|
return float(i)
|
|
462
495
|
|
|
@@ -470,12 +503,17 @@ def build_element(element: Expando, items: List[Job], item_name: str, defaults:
|
|
|
470
503
|
if category in COMPUTED_CATEGORIES:
|
|
471
504
|
continue
|
|
472
505
|
category_key = category if category != "cores" else "requested_cores"
|
|
473
|
-
category_value = assertNonnegative(
|
|
506
|
+
category_value = assertNonnegative(
|
|
507
|
+
float(item.get(category_key, defaults[category])), category
|
|
508
|
+
)
|
|
474
509
|
values.append(category_value)
|
|
475
510
|
|
|
476
511
|
for index in range(0, len(item_values[CATEGORIES[0]])):
|
|
477
512
|
# For each item, compute the computed categories
|
|
478
|
-
item_values["wait"].append(
|
|
513
|
+
item_values["wait"].append(
|
|
514
|
+
item_values["time"][index] * item_values["cores"][index]
|
|
515
|
+
- item_values["clock"][index]
|
|
516
|
+
)
|
|
479
517
|
|
|
480
518
|
for category, values in item_values.items():
|
|
481
519
|
values.sort()
|
|
@@ -485,10 +523,7 @@ def build_element(element: Expando, items: List[Job], item_name: str, defaults:
|
|
|
485
523
|
for k, v in item_values.items():
|
|
486
524
|
v.append(0)
|
|
487
525
|
|
|
488
|
-
item_element = Expando(
|
|
489
|
-
total_number=float(len(items)),
|
|
490
|
-
name=item_name
|
|
491
|
-
)
|
|
526
|
+
item_element = Expando(total_number=float(len(items)), name=item_name)
|
|
492
527
|
|
|
493
528
|
for category, values in item_values.items():
|
|
494
529
|
item_element["total_" + category] = float(sum(values))
|
|
@@ -504,7 +539,7 @@ def build_element(element: Expando, items: List[Job], item_name: str, defaults:
|
|
|
504
539
|
|
|
505
540
|
def create_summary(
|
|
506
541
|
element: Expando,
|
|
507
|
-
containingItems:
|
|
542
|
+
containingItems: list[Expando],
|
|
508
543
|
containingItemName: str,
|
|
509
544
|
count_contained: Callable[[Expando], int],
|
|
510
545
|
) -> None:
|
|
@@ -599,7 +634,7 @@ def process_data(config: Config, stats: Expando) -> Expando:
|
|
|
599
634
|
build_element(collatedStatsTag, jobs, "jobs", defaults),
|
|
600
635
|
getattr(stats, "workers", []),
|
|
601
636
|
"worker",
|
|
602
|
-
lambda worker: getattr(worker, "jobs_run", 0)
|
|
637
|
+
lambda worker: getattr(worker, "jobs_run", 0),
|
|
603
638
|
)
|
|
604
639
|
# Get info for each job
|
|
605
640
|
jobNames = set()
|
|
@@ -684,7 +719,9 @@ def main() -> None:
|
|
|
684
719
|
|
|
685
720
|
for c in options.categories.split(","):
|
|
686
721
|
if c.strip().lower() not in CATEGORIES:
|
|
687
|
-
logger.critical(
|
|
722
|
+
logger.critical(
|
|
723
|
+
"Cannot use category %s, options are: %s", c.strip().lower(), CATEGORIES
|
|
724
|
+
)
|
|
688
725
|
sys.exit(1)
|
|
689
726
|
options.categories = [x.strip().lower() for x in options.categories.split(",")]
|
|
690
727
|
|
|
@@ -696,7 +733,9 @@ def main() -> None:
|
|
|
696
733
|
except NoSuchJobStoreException:
|
|
697
734
|
logger.critical("The job store %s does not exist", config.jobStore)
|
|
698
735
|
sys.exit(1)
|
|
699
|
-
logger.info(
|
|
736
|
+
logger.info(
|
|
737
|
+
"Gathering stats from jobstore... depending on the number of jobs, this may take a while (e.g. 10 jobs ~= 3 seconds; 100,000 jobs ~= 3,000 seconds or 50 minutes)."
|
|
738
|
+
)
|
|
700
739
|
stats = get_stats(jobStore)
|
|
701
740
|
collatedStatsTag = process_data(jobStore.config, stats)
|
|
702
741
|
report_data(collatedStatsTag, options)
|