toil 5.12.0__py3-none-any.whl → 6.1.0a1__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 +18 -13
- toil/batchSystems/abstractBatchSystem.py +21 -10
- toil/batchSystems/abstractGridEngineBatchSystem.py +2 -2
- toil/batchSystems/awsBatch.py +14 -14
- toil/batchSystems/contained_executor.py +3 -3
- toil/batchSystems/htcondor.py +0 -1
- toil/batchSystems/kubernetes.py +34 -31
- toil/batchSystems/local_support.py +3 -1
- toil/batchSystems/mesos/batchSystem.py +7 -7
- toil/batchSystems/options.py +32 -83
- toil/batchSystems/registry.py +104 -23
- toil/batchSystems/singleMachine.py +16 -13
- toil/batchSystems/slurm.py +3 -3
- toil/batchSystems/torque.py +0 -1
- toil/bus.py +6 -8
- toil/common.py +532 -743
- toil/cwl/__init__.py +28 -32
- toil/cwl/cwltoil.py +523 -520
- toil/cwl/utils.py +55 -10
- toil/fileStores/__init__.py +2 -2
- toil/fileStores/abstractFileStore.py +36 -11
- toil/fileStores/cachingFileStore.py +607 -530
- toil/fileStores/nonCachingFileStore.py +43 -10
- toil/job.py +140 -75
- toil/jobStores/abstractJobStore.py +147 -79
- toil/jobStores/aws/jobStore.py +23 -9
- toil/jobStores/aws/utils.py +1 -2
- toil/jobStores/fileJobStore.py +117 -19
- toil/jobStores/googleJobStore.py +16 -7
- toil/jobStores/utils.py +5 -6
- toil/leader.py +71 -43
- toil/lib/accelerators.py +10 -5
- toil/lib/aws/__init__.py +3 -14
- toil/lib/aws/ami.py +22 -9
- toil/lib/aws/iam.py +21 -13
- toil/lib/aws/session.py +2 -16
- toil/lib/aws/utils.py +4 -5
- toil/lib/compatibility.py +1 -1
- toil/lib/conversions.py +7 -3
- toil/lib/docker.py +22 -23
- toil/lib/ec2.py +10 -6
- toil/lib/ec2nodes.py +106 -100
- toil/lib/encryption/_nacl.py +2 -1
- toil/lib/generatedEC2Lists.py +325 -18
- toil/lib/io.py +21 -0
- toil/lib/misc.py +1 -1
- toil/lib/resources.py +1 -1
- toil/lib/threading.py +74 -26
- toil/options/common.py +738 -0
- toil/options/cwl.py +336 -0
- toil/options/wdl.py +32 -0
- toil/provisioners/abstractProvisioner.py +1 -4
- toil/provisioners/aws/__init__.py +3 -6
- toil/provisioners/aws/awsProvisioner.py +6 -0
- toil/provisioners/clusterScaler.py +3 -2
- toil/provisioners/gceProvisioner.py +2 -2
- toil/realtimeLogger.py +2 -1
- toil/resource.py +24 -18
- toil/server/app.py +2 -3
- toil/server/cli/wes_cwl_runner.py +4 -4
- toil/server/utils.py +1 -1
- toil/server/wes/abstract_backend.py +3 -2
- toil/server/wes/amazon_wes_utils.py +5 -4
- toil/server/wes/tasks.py +2 -3
- toil/server/wes/toil_backend.py +2 -10
- toil/server/wsgi_app.py +2 -0
- toil/serviceManager.py +12 -10
- toil/statsAndLogging.py +5 -1
- toil/test/__init__.py +29 -54
- toil/test/batchSystems/batchSystemTest.py +11 -111
- toil/test/batchSystems/test_slurm.py +3 -2
- toil/test/cwl/cwlTest.py +213 -90
- toil/test/cwl/glob_dir.cwl +15 -0
- toil/test/cwl/preemptible.cwl +21 -0
- toil/test/cwl/preemptible_expression.cwl +28 -0
- toil/test/cwl/revsort.cwl +1 -1
- toil/test/cwl/revsort2.cwl +1 -1
- toil/test/docs/scriptsTest.py +0 -1
- toil/test/jobStores/jobStoreTest.py +27 -16
- toil/test/lib/aws/test_iam.py +4 -14
- toil/test/lib/aws/test_utils.py +0 -3
- toil/test/lib/dockerTest.py +4 -4
- toil/test/lib/test_ec2.py +11 -16
- toil/test/mesos/helloWorld.py +4 -5
- toil/test/mesos/stress.py +1 -1
- toil/test/provisioners/aws/awsProvisionerTest.py +9 -5
- toil/test/provisioners/clusterScalerTest.py +6 -4
- toil/test/provisioners/clusterTest.py +14 -3
- toil/test/provisioners/gceProvisionerTest.py +0 -6
- toil/test/provisioners/restartScript.py +3 -2
- toil/test/server/serverTest.py +1 -1
- toil/test/sort/restart_sort.py +2 -1
- toil/test/sort/sort.py +2 -1
- toil/test/sort/sortTest.py +2 -13
- toil/test/src/autoDeploymentTest.py +45 -45
- toil/test/src/busTest.py +5 -5
- toil/test/src/checkpointTest.py +2 -2
- toil/test/src/deferredFunctionTest.py +1 -1
- toil/test/src/fileStoreTest.py +32 -16
- toil/test/src/helloWorldTest.py +1 -1
- toil/test/src/importExportFileTest.py +1 -1
- toil/test/src/jobDescriptionTest.py +2 -1
- toil/test/src/jobServiceTest.py +1 -1
- toil/test/src/jobTest.py +18 -18
- toil/test/src/miscTests.py +5 -3
- toil/test/src/promisedRequirementTest.py +3 -3
- toil/test/src/realtimeLoggerTest.py +1 -1
- toil/test/src/resourceTest.py +2 -2
- toil/test/src/restartDAGTest.py +1 -1
- toil/test/src/resumabilityTest.py +36 -2
- toil/test/src/retainTempDirTest.py +1 -1
- toil/test/src/systemTest.py +2 -2
- toil/test/src/toilContextManagerTest.py +2 -2
- toil/test/src/userDefinedJobArgTypeTest.py +1 -1
- toil/test/utils/toilDebugTest.py +98 -32
- toil/test/utils/toilKillTest.py +2 -2
- toil/test/utils/utilsTest.py +20 -0
- toil/test/wdl/wdltoil_test.py +148 -45
- toil/toilState.py +7 -6
- toil/utils/toilClean.py +1 -1
- toil/utils/toilConfig.py +36 -0
- toil/utils/toilDebugFile.py +60 -33
- toil/utils/toilDebugJob.py +39 -12
- toil/utils/toilDestroyCluster.py +1 -1
- toil/utils/toilKill.py +1 -1
- toil/utils/toilLaunchCluster.py +13 -2
- toil/utils/toilMain.py +3 -2
- toil/utils/toilRsyncCluster.py +1 -1
- toil/utils/toilSshCluster.py +1 -1
- toil/utils/toilStats.py +240 -143
- toil/utils/toilStatus.py +1 -4
- toil/version.py +11 -11
- toil/wdl/utils.py +2 -122
- toil/wdl/wdltoil.py +999 -386
- toil/worker.py +25 -31
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/METADATA +60 -53
- toil-6.1.0a1.dist-info/RECORD +237 -0
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/WHEEL +1 -1
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/entry_points.txt +0 -1
- toil/batchSystems/parasol.py +0 -379
- toil/batchSystems/tes.py +0 -459
- toil/test/batchSystems/parasolTestSupport.py +0 -117
- toil/test/wdl/builtinTest.py +0 -506
- toil/test/wdl/conftest.py +0 -23
- toil/test/wdl/toilwdlTest.py +0 -522
- toil/wdl/toilwdl.py +0 -141
- toil/wdl/versions/dev.py +0 -107
- toil/wdl/versions/draft2.py +0 -980
- toil/wdl/versions/v1.py +0 -794
- toil/wdl/wdl_analysis.py +0 -116
- toil/wdl/wdl_functions.py +0 -997
- toil/wdl/wdl_synthesis.py +0 -1011
- toil/wdl/wdl_types.py +0 -243
- toil-5.12.0.dist-info/RECORD +0 -244
- /toil/{wdl/versions → options}/__init__.py +0 -0
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/LICENSE +0 -0
- {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/top_level.txt +0 -0
toil/utils/toilStats.py
CHANGED
|
@@ -31,6 +31,7 @@ class ColumnWidths:
|
|
|
31
31
|
"""
|
|
32
32
|
Convenience object that stores the width of columns for printing. Helps make things pretty.
|
|
33
33
|
"""
|
|
34
|
+
|
|
34
35
|
def __init__(self) -> None:
|
|
35
36
|
self.categories = ["time", "clock", "wait", "memory"]
|
|
36
37
|
self.fields_count = ["count", "min", "med", "ave", "max", "total"]
|
|
@@ -41,11 +42,10 @@ class ColumnWidths:
|
|
|
41
42
|
self.setWidth(category, field, 8)
|
|
42
43
|
|
|
43
44
|
def title(self, category: str) -> int:
|
|
44
|
-
"""
|
|
45
|
-
"""
|
|
45
|
+
"""Return the total printed length of this category item."""
|
|
46
46
|
return sum(self.getWidth(category, x) for x in self.fields)
|
|
47
47
|
|
|
48
|
-
def getWidth(self, category: str, field: str
|
|
48
|
+
def getWidth(self, category: str, field: str) -> int:
|
|
49
49
|
category = category.lower()
|
|
50
50
|
return self.data[f"{category}_{field}"]
|
|
51
51
|
|
|
@@ -56,7 +56,7 @@ class ColumnWidths:
|
|
|
56
56
|
def report(self) -> None:
|
|
57
57
|
for c in self.categories:
|
|
58
58
|
for f in self.fields:
|
|
59
|
-
print(
|
|
59
|
+
print("%s %s %d" % (c, f, self.getWidth(c, f)))
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
def padStr(s: str, field: Optional[int] = None) -> str:
|
|
@@ -87,43 +87,46 @@ def prettyMemory(k: float, field: Optional[int] = None, isBytes: bool = False) -
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def prettyTime(t: float, field: Optional[int] = None) -> str:
|
|
90
|
-
"""
|
|
91
|
-
"""
|
|
90
|
+
"""Given input t as seconds, return a nicely formatted string."""
|
|
92
91
|
from math import floor
|
|
92
|
+
|
|
93
93
|
pluralDict = {True: "s", False: ""}
|
|
94
94
|
if t < 120:
|
|
95
95
|
return padStr("%ds" % t, field)
|
|
96
96
|
if t < 120 * 60:
|
|
97
|
-
m = floor(t / 60.)
|
|
97
|
+
m = floor(t / 60.0)
|
|
98
98
|
s = t % 60
|
|
99
99
|
return padStr("%dm%ds" % (m, s), field)
|
|
100
100
|
if t < 25 * 60 * 60:
|
|
101
|
-
h = floor(t / 60. / 60.)
|
|
102
|
-
m = floor((t - (h * 60. * 60.)) / 60.)
|
|
101
|
+
h = floor(t / 60.0 / 60.0)
|
|
102
|
+
m = floor((t - (h * 60.0 * 60.0)) / 60.0)
|
|
103
103
|
s = t % 60
|
|
104
104
|
return padStr("%dh%gm%ds" % (h, m, s), field)
|
|
105
105
|
if t < 7 * 24 * 60 * 60:
|
|
106
|
-
d = floor(t / 24. / 60. / 60.)
|
|
107
|
-
h = floor((t - (d * 24. * 60. * 60.)) / 60. / 60.)
|
|
108
|
-
m = floor((t
|
|
109
|
-
- (d * 24. * 60. * 60.)
|
|
110
|
-
- (h * 60. * 60.)) / 60.)
|
|
106
|
+
d = floor(t / 24.0 / 60.0 / 60.0)
|
|
107
|
+
h = floor((t - (d * 24.0 * 60.0 * 60.0)) / 60.0 / 60.0)
|
|
108
|
+
m = floor((t - (d * 24.0 * 60.0 * 60.0) - (h * 60.0 * 60.0)) / 60.0)
|
|
111
109
|
s = t % 60
|
|
112
110
|
dPlural = pluralDict[d > 1]
|
|
113
111
|
return padStr("%dday%s%dh%dm%ds" % (d, dPlural, h, m, s), field)
|
|
114
|
-
w = floor(t / 7. / 24. / 60. / 60.)
|
|
115
|
-
d = floor((t - (w * 7 * 24 * 60 * 60)) / 24. / 60. / 60.)
|
|
116
|
-
h = floor(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
m = floor(
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
w = floor(t / 7.0 / 24.0 / 60.0 / 60.0)
|
|
113
|
+
d = floor((t - (w * 7 * 24 * 60 * 60)) / 24.0 / 60.0 / 60.0)
|
|
114
|
+
h = floor(
|
|
115
|
+
(t - (w * 7.0 * 24.0 * 60.0 * 60.0) - (d * 24.0 * 60.0 * 60.0)) / 60.0 / 60.0
|
|
116
|
+
)
|
|
117
|
+
m = floor(
|
|
118
|
+
(
|
|
119
|
+
t
|
|
120
|
+
- (w * 7.0 * 24.0 * 60.0 * 60.0)
|
|
121
|
+
- (d * 24.0 * 60.0 * 60.0)
|
|
122
|
+
- (h * 60.0 * 60.0)
|
|
123
|
+
)
|
|
124
|
+
/ 60.0
|
|
125
|
+
)
|
|
122
126
|
s = t % 60
|
|
123
127
|
wPlural = pluralDict[w > 1]
|
|
124
128
|
dPlural = pluralDict[d > 1]
|
|
125
|
-
return padStr("%dweek%s%dday%s%dh%dm%ds" % (w, wPlural, d,
|
|
126
|
-
dPlural, h, m, s), field)
|
|
129
|
+
return padStr("%dweek%s%dday%s%dh%dm%ds" % (w, wPlural, d, dPlural, h, m, s), field)
|
|
127
130
|
|
|
128
131
|
|
|
129
132
|
def reportTime(t: float, options: Namespace, field: Optional[int] = None) -> str:
|
|
@@ -135,13 +138,15 @@ def reportTime(t: float, options: Namespace, field: Optional[int] = None) -> str
|
|
|
135
138
|
return "%.2f" % t
|
|
136
139
|
|
|
137
140
|
|
|
138
|
-
def reportMemory(
|
|
141
|
+
def reportMemory(
|
|
142
|
+
k: float, options: Namespace, field: Optional[int] = None, isBytes: bool = False
|
|
143
|
+
) -> str:
|
|
139
144
|
"""Given k kilobytes, report back the correct format as string."""
|
|
140
145
|
if options.pretty:
|
|
141
146
|
return prettyMemory(int(k), field=field, isBytes=isBytes)
|
|
142
147
|
else:
|
|
143
148
|
if isBytes:
|
|
144
|
-
k /= 1024.
|
|
149
|
+
k /= 1024.0
|
|
145
150
|
if field is not None:
|
|
146
151
|
return "%*dK" % (field - 1, k) # -1 for the "K"
|
|
147
152
|
else:
|
|
@@ -153,9 +158,13 @@ def reportNumber(n: float, field: Optional[int] = None) -> str:
|
|
|
153
158
|
return "%*g" % (field, n) if field else "%g" % n
|
|
154
159
|
|
|
155
160
|
|
|
156
|
-
def sprintTag(
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
def sprintTag(
|
|
162
|
+
key: str,
|
|
163
|
+
tag: Expando,
|
|
164
|
+
options: Namespace,
|
|
165
|
+
columnWidths: Optional[ColumnWidths] = None,
|
|
166
|
+
) -> str:
|
|
167
|
+
"""Generate a pretty-print ready string from a JTTag()."""
|
|
159
168
|
if columnWidths is None:
|
|
160
169
|
columnWidths = ColumnWidths()
|
|
161
170
|
header = " %7s " % decorateTitle("Count", options)
|
|
@@ -163,16 +172,23 @@ def sprintTag(key: str, tag: Expando, options: Namespace, columnWidths: Optional
|
|
|
163
172
|
tag_str = f" {reportNumber(n=tag.total_number, field=7)}"
|
|
164
173
|
out_str = ""
|
|
165
174
|
if key == "job":
|
|
166
|
-
out_str += " {:<12} | {:>7}{:>7}{:>7}{:>7}\n".format(
|
|
167
|
-
|
|
175
|
+
out_str += " {:<12} | {:>7}{:>7}{:>7}{:>7}\n".format(
|
|
176
|
+
"Worker Jobs", "min", "med", "ave", "max"
|
|
177
|
+
)
|
|
168
178
|
worker_str = "%s| " % (" " * 14)
|
|
169
|
-
for t in [
|
|
170
|
-
|
|
179
|
+
for t in [
|
|
180
|
+
tag.min_number_per_worker,
|
|
181
|
+
tag.median_number_per_worker,
|
|
182
|
+
tag.average_number_per_worker,
|
|
183
|
+
tag.max_number_per_worker,
|
|
184
|
+
]:
|
|
171
185
|
worker_str += reportNumber(n=t, field=7)
|
|
172
186
|
out_str += worker_str + "\n"
|
|
173
187
|
if "time" in options.categories:
|
|
174
|
-
header += "| %*s " % (
|
|
175
|
-
|
|
188
|
+
header += "| %*s " % (
|
|
189
|
+
columnWidths.title("time"),
|
|
190
|
+
decorateTitle("Time", options),
|
|
191
|
+
)
|
|
176
192
|
sub_header += decorateSubHeader("Time", columnWidths, options)
|
|
177
193
|
tag_str += " | "
|
|
178
194
|
for t, width in [
|
|
@@ -181,11 +197,13 @@ def sprintTag(key: str, tag: Expando, options: Namespace, columnWidths: Optional
|
|
|
181
197
|
(tag.average_time, columnWidths.getWidth("time", "ave")),
|
|
182
198
|
(tag.max_time, columnWidths.getWidth("time", "max")),
|
|
183
199
|
(tag.total_time, columnWidths.getWidth("time", "total")),
|
|
184
|
-
|
|
200
|
+
]:
|
|
185
201
|
tag_str += reportTime(t, options, field=width)
|
|
186
202
|
if "clock" in options.categories:
|
|
187
|
-
header += "| %*s " % (
|
|
188
|
-
|
|
203
|
+
header += "| %*s " % (
|
|
204
|
+
columnWidths.title("clock"),
|
|
205
|
+
decorateTitle("Clock", options),
|
|
206
|
+
)
|
|
189
207
|
sub_header += decorateSubHeader("Clock", columnWidths, options)
|
|
190
208
|
tag_str += " | "
|
|
191
209
|
for t, width in [
|
|
@@ -194,11 +212,13 @@ def sprintTag(key: str, tag: Expando, options: Namespace, columnWidths: Optional
|
|
|
194
212
|
(tag.average_clock, columnWidths.getWidth("clock", "ave")),
|
|
195
213
|
(tag.max_clock, columnWidths.getWidth("clock", "max")),
|
|
196
214
|
(tag.total_clock, columnWidths.getWidth("clock", "total")),
|
|
197
|
-
|
|
215
|
+
]:
|
|
198
216
|
tag_str += reportTime(t, options, field=width)
|
|
199
217
|
if "wait" in options.categories:
|
|
200
|
-
header += "| %*s " % (
|
|
201
|
-
|
|
218
|
+
header += "| %*s " % (
|
|
219
|
+
columnWidths.title("wait"),
|
|
220
|
+
decorateTitle("Wait", options),
|
|
221
|
+
)
|
|
202
222
|
sub_header += decorateSubHeader("Wait", columnWidths, options)
|
|
203
223
|
tag_str += " | "
|
|
204
224
|
for t, width in [
|
|
@@ -207,11 +227,13 @@ def sprintTag(key: str, tag: Expando, options: Namespace, columnWidths: Optional
|
|
|
207
227
|
(tag.average_wait, columnWidths.getWidth("wait", "ave")),
|
|
208
228
|
(tag.max_wait, columnWidths.getWidth("wait", "max")),
|
|
209
229
|
(tag.total_wait, columnWidths.getWidth("wait", "total")),
|
|
210
|
-
|
|
230
|
+
]:
|
|
211
231
|
tag_str += reportTime(t, options, field=width)
|
|
212
232
|
if "memory" in options.categories:
|
|
213
|
-
header += "| %*s " % (
|
|
214
|
-
|
|
233
|
+
header += "| %*s " % (
|
|
234
|
+
columnWidths.title("memory"),
|
|
235
|
+
decorateTitle("Memory", options),
|
|
236
|
+
)
|
|
215
237
|
sub_header += decorateSubHeader("Memory", columnWidths, options)
|
|
216
238
|
tag_str += " | "
|
|
217
239
|
for t, width in [
|
|
@@ -220,40 +242,50 @@ def sprintTag(key: str, tag: Expando, options: Namespace, columnWidths: Optional
|
|
|
220
242
|
(tag.average_memory, columnWidths.getWidth("memory", "ave")),
|
|
221
243
|
(tag.max_memory, columnWidths.getWidth("memory", "max")),
|
|
222
244
|
(tag.total_memory, columnWidths.getWidth("memory", "total")),
|
|
223
|
-
|
|
245
|
+
]:
|
|
224
246
|
tag_str += reportMemory(t, options, field=width)
|
|
225
247
|
out_str += header + "\n"
|
|
226
248
|
out_str += sub_header + "\n"
|
|
227
249
|
out_str += tag_str + "\n"
|
|
228
250
|
return out_str
|
|
229
251
|
|
|
252
|
+
|
|
230
253
|
def decorateTitle(title: str, options: Namespace) -> str:
|
|
231
|
-
"""
|
|
232
|
-
"""
|
|
254
|
+
"""Add a marker to TITLE if the TITLE is sorted on."""
|
|
233
255
|
if title.lower() == options.sortCategory:
|
|
234
256
|
return "%s*" % title
|
|
235
257
|
else:
|
|
236
258
|
return title
|
|
237
259
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
260
|
+
|
|
261
|
+
def decorateSubHeader(
|
|
262
|
+
title: str, columnWidths: ColumnWidths, options: Namespace
|
|
263
|
+
) -> str:
|
|
264
|
+
"""Add a marker to the correct field if the TITLE is sorted on."""
|
|
241
265
|
title = title.lower()
|
|
242
266
|
if title != options.sortCategory:
|
|
243
267
|
s = "| %*s%*s%*s%*s%*s " % (
|
|
244
|
-
columnWidths.getWidth(title, "min"),
|
|
245
|
-
|
|
246
|
-
columnWidths.getWidth(title, "
|
|
247
|
-
|
|
248
|
-
columnWidths.getWidth(title, "
|
|
268
|
+
columnWidths.getWidth(title, "min"),
|
|
269
|
+
"min",
|
|
270
|
+
columnWidths.getWidth(title, "med"),
|
|
271
|
+
"med",
|
|
272
|
+
columnWidths.getWidth(title, "ave"),
|
|
273
|
+
"ave",
|
|
274
|
+
columnWidths.getWidth(title, "max"),
|
|
275
|
+
"max",
|
|
276
|
+
columnWidths.getWidth(title, "total"),
|
|
277
|
+
"total",
|
|
278
|
+
)
|
|
249
279
|
return s
|
|
250
280
|
else:
|
|
251
281
|
s = "| "
|
|
252
|
-
for field, width in [
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
282
|
+
for field, width in [
|
|
283
|
+
("min", columnWidths.getWidth(title, "min")),
|
|
284
|
+
("med", columnWidths.getWidth(title, "med")),
|
|
285
|
+
("ave", columnWidths.getWidth(title, "ave")),
|
|
286
|
+
("max", columnWidths.getWidth(title, "max")),
|
|
287
|
+
("total", columnWidths.getWidth(title, "total")),
|
|
288
|
+
]:
|
|
257
289
|
if options.sortField == field:
|
|
258
290
|
s += "%*s*" % (width - 1, field)
|
|
259
291
|
else:
|
|
@@ -272,48 +304,60 @@ def get(tree: Expando, name: str) -> float:
|
|
|
272
304
|
|
|
273
305
|
def sortJobs(jobTypes: List[Any], options: Namespace) -> List[Any]:
|
|
274
306
|
"""Return a jobTypes all sorted."""
|
|
275
|
-
longforms = {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
307
|
+
longforms = {
|
|
308
|
+
"med": "median",
|
|
309
|
+
"ave": "average",
|
|
310
|
+
"min": "min",
|
|
311
|
+
"total": "total",
|
|
312
|
+
"max": "max",
|
|
313
|
+
}
|
|
280
314
|
sortField = longforms[options.sortField]
|
|
281
|
-
if (
|
|
282
|
-
options.sortCategory == "
|
|
283
|
-
options.sortCategory == "
|
|
284
|
-
options.sortCategory == "
|
|
285
|
-
|
|
315
|
+
if (
|
|
316
|
+
options.sortCategory == "time"
|
|
317
|
+
or options.sortCategory == "clock"
|
|
318
|
+
or options.sortCategory == "wait"
|
|
319
|
+
or options.sortCategory == "memory"
|
|
320
|
+
):
|
|
286
321
|
return sorted(
|
|
287
322
|
jobTypes,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
reverse=options.sortReverse)
|
|
323
|
+
key=lambda tag: getattr(tag, "%s_%s" % (sortField, options.sortCategory)),
|
|
324
|
+
reverse=options.sortReverse,
|
|
325
|
+
)
|
|
292
326
|
elif options.sortCategory == "alpha":
|
|
293
327
|
return sorted(
|
|
294
|
-
jobTypes,
|
|
295
|
-
|
|
328
|
+
jobTypes,
|
|
329
|
+
key=lambda tag: tag.name, # type: ignore
|
|
330
|
+
reverse=options.sortReverse,
|
|
331
|
+
)
|
|
296
332
|
elif options.sortCategory == "count":
|
|
297
|
-
return sorted(
|
|
298
|
-
|
|
333
|
+
return sorted(
|
|
334
|
+
jobTypes,
|
|
335
|
+
key=lambda tag: tag.total_number, # type: ignore
|
|
336
|
+
reverse=options.sortReverse,
|
|
337
|
+
)
|
|
299
338
|
|
|
300
339
|
# due to https://stackoverflow.com/questions/47149154
|
|
301
340
|
assert False
|
|
302
341
|
|
|
303
342
|
|
|
304
|
-
def reportPrettyData(
|
|
343
|
+
def reportPrettyData(
|
|
344
|
+
root: Expando,
|
|
345
|
+
worker: List[Job],
|
|
346
|
+
job: List[Job],
|
|
347
|
+
job_types: List[Any],
|
|
348
|
+
options: Namespace,
|
|
349
|
+
) -> str:
|
|
305
350
|
"""Print the important bits out."""
|
|
306
351
|
out_str = "Batch System: %s\n" % root.batch_system
|
|
307
|
-
out_str +=
|
|
308
|
-
"Max Cores: %s\n" % (
|
|
352
|
+
out_str += "Default Cores: %s Default Memory: %s\n" "Max Cores: %s\n" % (
|
|
309
353
|
reportNumber(n=get(root, "default_cores")),
|
|
310
354
|
reportMemory(get(root, "default_memory"), options, isBytes=True),
|
|
311
355
|
reportNumber(n=get(root, "max_cores")),
|
|
312
|
-
|
|
313
|
-
out_str +=
|
|
356
|
+
)
|
|
357
|
+
out_str += "Total Clock: {} Total Runtime: {}\n".format(
|
|
314
358
|
reportTime(get(root, "total_clock"), options),
|
|
315
359
|
reportTime(get(root, "total_run_time"), options),
|
|
316
|
-
|
|
360
|
+
)
|
|
317
361
|
job_types = sortJobs(job_types, options)
|
|
318
362
|
columnWidths = computeColumnWidths(job_types, worker, job, options)
|
|
319
363
|
out_str += "Worker\n"
|
|
@@ -327,9 +371,10 @@ def reportPrettyData(root: Expando, worker: List[Job], job: List[Job], job_types
|
|
|
327
371
|
return out_str
|
|
328
372
|
|
|
329
373
|
|
|
330
|
-
def computeColumnWidths(
|
|
331
|
-
|
|
332
|
-
|
|
374
|
+
def computeColumnWidths(
|
|
375
|
+
job_types: List[Any], worker: List[Job], job: List[Job], options: Expando
|
|
376
|
+
) -> ColumnWidths:
|
|
377
|
+
"""Return a ColumnWidths() object with the correct max widths."""
|
|
333
378
|
cw = ColumnWidths()
|
|
334
379
|
for t in job_types:
|
|
335
380
|
updateColumnWidths(t, cw, options)
|
|
@@ -339,34 +384,37 @@ def computeColumnWidths(job_types: List[Any], worker: List[Job], job: List[Job],
|
|
|
339
384
|
|
|
340
385
|
|
|
341
386
|
def updateColumnWidths(tag: Expando, cw: ColumnWidths, options: Expando) -> None:
|
|
342
|
-
"""
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
387
|
+
"""Update the column width attributes for this tag's fields."""
|
|
388
|
+
longforms = {
|
|
389
|
+
"med": "median",
|
|
390
|
+
"ave": "average",
|
|
391
|
+
"min": "min",
|
|
392
|
+
"total": "total",
|
|
393
|
+
"max": "max",
|
|
394
|
+
}
|
|
349
395
|
for category in ["time", "clock", "wait", "memory"]:
|
|
350
396
|
if category in options.categories:
|
|
351
397
|
for field in ["min", "med", "ave", "max", "total"]:
|
|
352
398
|
t = getattr(tag, f"{longforms[field]}_{category}")
|
|
353
399
|
if category in ["time", "clock", "wait"]:
|
|
354
|
-
s = reportTime(
|
|
355
|
-
|
|
400
|
+
s = reportTime(
|
|
401
|
+
t, options, field=cw.getWidth(category, field)
|
|
402
|
+
).strip()
|
|
356
403
|
else:
|
|
357
|
-
s = reportMemory(
|
|
358
|
-
|
|
404
|
+
s = reportMemory(
|
|
405
|
+
t, options, field=cw.getWidth(category, field), isBytes=True
|
|
406
|
+
).strip()
|
|
359
407
|
if len(s) >= cw.getWidth(category, field):
|
|
360
408
|
# this string is larger than max, width must be increased
|
|
361
409
|
cw.setWidth(category, field, len(s) + 1)
|
|
362
410
|
|
|
363
411
|
|
|
364
412
|
def buildElement(element: Expando, items: List[Job], itemName: str) -> Expando:
|
|
365
|
-
"""
|
|
366
|
-
|
|
413
|
+
"""Create an element for output."""
|
|
414
|
+
|
|
367
415
|
def assertNonnegative(i: float, name: str) -> float:
|
|
368
416
|
if i < 0:
|
|
369
|
-
raise RuntimeError("Negative value %s reported for %s" %(i,name)
|
|
417
|
+
raise RuntimeError("Negative value %s reported for %s" % (i, name))
|
|
370
418
|
else:
|
|
371
419
|
return float(i)
|
|
372
420
|
|
|
@@ -382,12 +430,14 @@ def buildElement(element: Expando, items: List[Job], itemName: str) -> Expando:
|
|
|
382
430
|
itemTimes.append(assertNonnegative(float(item.get("time", 0)), "time"))
|
|
383
431
|
itemClocks.append(assertNonnegative(float(item.get("clock", 0)), "clock"))
|
|
384
432
|
itemMemory.append(assertNonnegative(float(item.get("memory", 0)), "memory"))
|
|
385
|
-
totalCores += assertNonnegative(
|
|
433
|
+
totalCores += assertNonnegative(
|
|
434
|
+
float(item.get("requested_cores", 0)), "requested_cores"
|
|
435
|
+
)
|
|
386
436
|
|
|
387
437
|
assert len(itemClocks) == len(itemTimes) == len(itemMemory)
|
|
388
438
|
|
|
389
439
|
itemWaits = []
|
|
390
|
-
for index in range(0,len(itemTimes)):
|
|
440
|
+
for index in range(0, len(itemTimes)):
|
|
391
441
|
itemWaits.append(itemTimes[index] - itemClocks[index])
|
|
392
442
|
|
|
393
443
|
itemWaits.sort()
|
|
@@ -401,7 +451,7 @@ def buildElement(element: Expando, items: List[Job], itemName: str) -> Expando:
|
|
|
401
451
|
itemWaits.append(0)
|
|
402
452
|
itemMemory.append(0)
|
|
403
453
|
|
|
404
|
-
element[itemName]=Expando(
|
|
454
|
+
element[itemName] = Expando(
|
|
405
455
|
total_number=float(len(items)),
|
|
406
456
|
total_time=float(sum(itemTimes)),
|
|
407
457
|
median_time=float(itemTimes[len(itemTimes) // 2]),
|
|
@@ -424,26 +474,34 @@ def buildElement(element: Expando, items: List[Job], itemName: str) -> Expando:
|
|
|
424
474
|
min_memory=float(min(itemMemory)),
|
|
425
475
|
max_memory=float(max(itemMemory)),
|
|
426
476
|
total_cores=totalCores,
|
|
427
|
-
name=itemName
|
|
477
|
+
name=itemName,
|
|
428
478
|
)
|
|
429
479
|
return element[itemName]
|
|
430
480
|
|
|
431
481
|
|
|
432
|
-
def createSummary(
|
|
433
|
-
|
|
434
|
-
|
|
482
|
+
def createSummary(
|
|
483
|
+
element: Expando,
|
|
484
|
+
containingItems: List[Job],
|
|
485
|
+
containingItemName: str,
|
|
486
|
+
getFn: Callable[[Job], List[Optional[Job]]],
|
|
487
|
+
) -> None:
|
|
488
|
+
itemCounts = [len(getFn(containingItem)) for containingItem in containingItems]
|
|
435
489
|
itemCounts.sort()
|
|
436
490
|
if len(itemCounts) == 0:
|
|
437
491
|
itemCounts.append(0)
|
|
438
|
-
element["median_number_per_%s" % containingItemName] = itemCounts[
|
|
439
|
-
|
|
492
|
+
element["median_number_per_%s" % containingItemName] = itemCounts[
|
|
493
|
+
len(itemCounts) // 2
|
|
494
|
+
]
|
|
495
|
+
element["average_number_per_%s" % containingItemName] = float(
|
|
496
|
+
sum(itemCounts) / len(itemCounts)
|
|
497
|
+
)
|
|
440
498
|
element["min_number_per_%s" % containingItemName] = min(itemCounts)
|
|
441
499
|
element["max_number_per_%s" % containingItemName] = max(itemCounts)
|
|
442
500
|
|
|
443
501
|
|
|
444
502
|
def getStats(jobStore: AbstractJobStore) -> Expando:
|
|
445
|
-
"""
|
|
446
|
-
|
|
503
|
+
"""Collect and return the stats and config data."""
|
|
504
|
+
|
|
447
505
|
def aggregateStats(fileHandle: TextIO, aggregateObject: Expando) -> None:
|
|
448
506
|
try:
|
|
449
507
|
stats = json.load(fileHandle, object_hook=Expando)
|
|
@@ -451,9 +509,11 @@ def getStats(jobStore: AbstractJobStore) -> Expando:
|
|
|
451
509
|
if key in aggregateObject:
|
|
452
510
|
aggregateObject[key].append(stats[key])
|
|
453
511
|
else:
|
|
454
|
-
aggregateObject[key]=[stats[key]]
|
|
512
|
+
aggregateObject[key] = [stats[key]]
|
|
455
513
|
except ValueError:
|
|
456
|
-
logger.critical(
|
|
514
|
+
logger.critical(
|
|
515
|
+
"File %s contains corrupted json. Skipping file." % fileHandle
|
|
516
|
+
)
|
|
457
517
|
pass # The file is corrupted.
|
|
458
518
|
|
|
459
519
|
aggregateObject = Expando()
|
|
@@ -466,7 +526,7 @@ def processData(config: Config, stats: Expando) -> Expando:
|
|
|
466
526
|
"""
|
|
467
527
|
Collate the stats and report
|
|
468
528
|
"""
|
|
469
|
-
if
|
|
529
|
+
if "total_time" not in stats or "total_clock" not in stats:
|
|
470
530
|
# toil job not finished yet
|
|
471
531
|
stats.total_time = [0.0]
|
|
472
532
|
stats.total_clock = [0.0]
|
|
@@ -474,17 +534,18 @@ def processData(config: Config, stats: Expando) -> Expando:
|
|
|
474
534
|
stats.total_time = sum(float(number) for number in stats.total_time)
|
|
475
535
|
stats.total_clock = sum(float(number) for number in stats.total_clock)
|
|
476
536
|
|
|
477
|
-
collatedStatsTag = Expando(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
537
|
+
collatedStatsTag = Expando(
|
|
538
|
+
total_run_time=stats.total_time,
|
|
539
|
+
total_clock=stats.total_clock,
|
|
540
|
+
batch_system=config.batchSystem,
|
|
541
|
+
default_memory=str(config.defaultMemory),
|
|
542
|
+
default_cores=str(config.defaultCores),
|
|
543
|
+
max_cores=str(config.maxCores),
|
|
544
|
+
)
|
|
484
545
|
|
|
485
546
|
# Add worker info
|
|
486
|
-
worker = [_f for _f in getattr(stats,
|
|
487
|
-
jobs = [_f for _f in getattr(stats,
|
|
547
|
+
worker = [_f for _f in getattr(stats, "workers", []) if _f]
|
|
548
|
+
jobs = [_f for _f in getattr(stats, "jobs", []) if _f]
|
|
488
549
|
jobs = [item for sublist in jobs for item in sublist]
|
|
489
550
|
|
|
490
551
|
def fn4(job: Job) -> List[Optional[Job]]:
|
|
@@ -494,8 +555,12 @@ def processData(config: Config, stats: Expando) -> Expando:
|
|
|
494
555
|
return []
|
|
495
556
|
|
|
496
557
|
buildElement(collatedStatsTag, worker, "worker")
|
|
497
|
-
createSummary(
|
|
498
|
-
|
|
558
|
+
createSummary(
|
|
559
|
+
buildElement(collatedStatsTag, jobs, "jobs"),
|
|
560
|
+
getattr(stats, "workers", []),
|
|
561
|
+
"worker",
|
|
562
|
+
fn4,
|
|
563
|
+
)
|
|
499
564
|
# Get info for each job
|
|
500
565
|
jobNames = set()
|
|
501
566
|
for job in jobs:
|
|
@@ -503,7 +568,7 @@ def processData(config: Config, stats: Expando) -> Expando:
|
|
|
503
568
|
jobTypesTag = Expando()
|
|
504
569
|
collatedStatsTag.job_types = jobTypesTag
|
|
505
570
|
for jobName in jobNames:
|
|
506
|
-
jobTypes = [
|
|
571
|
+
jobTypes = [job for job in jobs if job.class_name == jobName]
|
|
507
572
|
buildElement(jobTypesTag, jobTypes, jobName)
|
|
508
573
|
collatedStatsTag.name = "collatedStatsTag"
|
|
509
574
|
return collatedStatsTag
|
|
@@ -512,9 +577,11 @@ def processData(config: Config, stats: Expando) -> Expando:
|
|
|
512
577
|
def reportData(tree: Expando, options: Namespace) -> None:
|
|
513
578
|
# Now dump it all out to file
|
|
514
579
|
if options.raw:
|
|
515
|
-
out_str = json.dumps(tree, indent=4, separators=(
|
|
580
|
+
out_str = json.dumps(tree, indent=4, separators=(",", ": "))
|
|
516
581
|
else:
|
|
517
|
-
out_str = reportPrettyData(
|
|
582
|
+
out_str = reportPrettyData(
|
|
583
|
+
tree, tree.worker, tree.jobs, tree.job_types.values(), options
|
|
584
|
+
)
|
|
518
585
|
if options.outputFile is not None:
|
|
519
586
|
with open(options.outputFile, "w") as f:
|
|
520
587
|
f.write(out_str)
|
|
@@ -524,32 +591,62 @@ def reportData(tree: Expando, options: Namespace) -> None:
|
|
|
524
591
|
|
|
525
592
|
category_choices = ["time", "clock", "wait", "memory"]
|
|
526
593
|
sort_category_choices = ["time", "clock", "wait", "memory", "alpha", "count"]
|
|
527
|
-
sort_field_choices = [
|
|
594
|
+
sort_field_choices = ["min", "med", "ave", "max", "total"]
|
|
528
595
|
|
|
529
596
|
|
|
530
597
|
def add_stats_options(parser: ArgumentParser) -> None:
|
|
531
|
-
parser.add_argument(
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
parser.add_argument(
|
|
541
|
-
|
|
598
|
+
parser.add_argument(
|
|
599
|
+
"--outputFile",
|
|
600
|
+
dest="outputFile",
|
|
601
|
+
default=None,
|
|
602
|
+
help="File in which to write results.",
|
|
603
|
+
)
|
|
604
|
+
parser.add_argument(
|
|
605
|
+
"--raw", action="store_true", default=False, help="Return raw json data."
|
|
606
|
+
)
|
|
607
|
+
parser.add_argument(
|
|
608
|
+
"--pretty",
|
|
609
|
+
"--human",
|
|
610
|
+
action="store_true",
|
|
611
|
+
default=False,
|
|
612
|
+
help="if not raw, prettify the numbers to be human readable.",
|
|
613
|
+
)
|
|
614
|
+
parser.add_argument(
|
|
615
|
+
"--sortReverse",
|
|
616
|
+
"--reverseSort",
|
|
617
|
+
default=False,
|
|
618
|
+
action="store_true",
|
|
619
|
+
help="Reverse sort.",
|
|
620
|
+
)
|
|
621
|
+
parser.add_argument(
|
|
622
|
+
"--categories",
|
|
623
|
+
default=",".join(category_choices),
|
|
624
|
+
type=str,
|
|
625
|
+
help=f"Comma separated list of any of the following: {category_choices}.",
|
|
626
|
+
)
|
|
627
|
+
parser.add_argument(
|
|
628
|
+
"--sortCategory",
|
|
629
|
+
default="time",
|
|
630
|
+
choices=sort_category_choices,
|
|
631
|
+
help=f"How to sort job categories. Choices: {sort_category_choices}. Default: time.",
|
|
632
|
+
)
|
|
633
|
+
parser.add_argument(
|
|
634
|
+
"--sortField",
|
|
635
|
+
default="med",
|
|
636
|
+
choices=sort_field_choices,
|
|
637
|
+
help=f"How to sort job fields. Choices: {sort_field_choices}. Default: med.",
|
|
638
|
+
)
|
|
542
639
|
|
|
543
640
|
|
|
544
641
|
def main() -> None:
|
|
545
642
|
"""Reports stats on the workflow, use with --stats option to toil."""
|
|
546
|
-
parser = parser_with_common_options()
|
|
643
|
+
parser = parser_with_common_options(prog="toil stats")
|
|
547
644
|
add_stats_options(parser)
|
|
548
645
|
options = parser.parse_args()
|
|
549
646
|
|
|
550
647
|
for c in options.categories.split(","):
|
|
551
648
|
if c.strip() not in category_choices:
|
|
552
|
-
raise ValueError(f
|
|
649
|
+
raise ValueError(f"{c} not in {category_choices}!")
|
|
553
650
|
options.categories = [x.strip().lower() for x in options.categories.split(",")]
|
|
554
651
|
|
|
555
652
|
set_logging_from_options(options)
|