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.
Files changed (157) hide show
  1. toil/__init__.py +18 -13
  2. toil/batchSystems/abstractBatchSystem.py +21 -10
  3. toil/batchSystems/abstractGridEngineBatchSystem.py +2 -2
  4. toil/batchSystems/awsBatch.py +14 -14
  5. toil/batchSystems/contained_executor.py +3 -3
  6. toil/batchSystems/htcondor.py +0 -1
  7. toil/batchSystems/kubernetes.py +34 -31
  8. toil/batchSystems/local_support.py +3 -1
  9. toil/batchSystems/mesos/batchSystem.py +7 -7
  10. toil/batchSystems/options.py +32 -83
  11. toil/batchSystems/registry.py +104 -23
  12. toil/batchSystems/singleMachine.py +16 -13
  13. toil/batchSystems/slurm.py +3 -3
  14. toil/batchSystems/torque.py +0 -1
  15. toil/bus.py +6 -8
  16. toil/common.py +532 -743
  17. toil/cwl/__init__.py +28 -32
  18. toil/cwl/cwltoil.py +523 -520
  19. toil/cwl/utils.py +55 -10
  20. toil/fileStores/__init__.py +2 -2
  21. toil/fileStores/abstractFileStore.py +36 -11
  22. toil/fileStores/cachingFileStore.py +607 -530
  23. toil/fileStores/nonCachingFileStore.py +43 -10
  24. toil/job.py +140 -75
  25. toil/jobStores/abstractJobStore.py +147 -79
  26. toil/jobStores/aws/jobStore.py +23 -9
  27. toil/jobStores/aws/utils.py +1 -2
  28. toil/jobStores/fileJobStore.py +117 -19
  29. toil/jobStores/googleJobStore.py +16 -7
  30. toil/jobStores/utils.py +5 -6
  31. toil/leader.py +71 -43
  32. toil/lib/accelerators.py +10 -5
  33. toil/lib/aws/__init__.py +3 -14
  34. toil/lib/aws/ami.py +22 -9
  35. toil/lib/aws/iam.py +21 -13
  36. toil/lib/aws/session.py +2 -16
  37. toil/lib/aws/utils.py +4 -5
  38. toil/lib/compatibility.py +1 -1
  39. toil/lib/conversions.py +7 -3
  40. toil/lib/docker.py +22 -23
  41. toil/lib/ec2.py +10 -6
  42. toil/lib/ec2nodes.py +106 -100
  43. toil/lib/encryption/_nacl.py +2 -1
  44. toil/lib/generatedEC2Lists.py +325 -18
  45. toil/lib/io.py +21 -0
  46. toil/lib/misc.py +1 -1
  47. toil/lib/resources.py +1 -1
  48. toil/lib/threading.py +74 -26
  49. toil/options/common.py +738 -0
  50. toil/options/cwl.py +336 -0
  51. toil/options/wdl.py +32 -0
  52. toil/provisioners/abstractProvisioner.py +1 -4
  53. toil/provisioners/aws/__init__.py +3 -6
  54. toil/provisioners/aws/awsProvisioner.py +6 -0
  55. toil/provisioners/clusterScaler.py +3 -2
  56. toil/provisioners/gceProvisioner.py +2 -2
  57. toil/realtimeLogger.py +2 -1
  58. toil/resource.py +24 -18
  59. toil/server/app.py +2 -3
  60. toil/server/cli/wes_cwl_runner.py +4 -4
  61. toil/server/utils.py +1 -1
  62. toil/server/wes/abstract_backend.py +3 -2
  63. toil/server/wes/amazon_wes_utils.py +5 -4
  64. toil/server/wes/tasks.py +2 -3
  65. toil/server/wes/toil_backend.py +2 -10
  66. toil/server/wsgi_app.py +2 -0
  67. toil/serviceManager.py +12 -10
  68. toil/statsAndLogging.py +5 -1
  69. toil/test/__init__.py +29 -54
  70. toil/test/batchSystems/batchSystemTest.py +11 -111
  71. toil/test/batchSystems/test_slurm.py +3 -2
  72. toil/test/cwl/cwlTest.py +213 -90
  73. toil/test/cwl/glob_dir.cwl +15 -0
  74. toil/test/cwl/preemptible.cwl +21 -0
  75. toil/test/cwl/preemptible_expression.cwl +28 -0
  76. toil/test/cwl/revsort.cwl +1 -1
  77. toil/test/cwl/revsort2.cwl +1 -1
  78. toil/test/docs/scriptsTest.py +0 -1
  79. toil/test/jobStores/jobStoreTest.py +27 -16
  80. toil/test/lib/aws/test_iam.py +4 -14
  81. toil/test/lib/aws/test_utils.py +0 -3
  82. toil/test/lib/dockerTest.py +4 -4
  83. toil/test/lib/test_ec2.py +11 -16
  84. toil/test/mesos/helloWorld.py +4 -5
  85. toil/test/mesos/stress.py +1 -1
  86. toil/test/provisioners/aws/awsProvisionerTest.py +9 -5
  87. toil/test/provisioners/clusterScalerTest.py +6 -4
  88. toil/test/provisioners/clusterTest.py +14 -3
  89. toil/test/provisioners/gceProvisionerTest.py +0 -6
  90. toil/test/provisioners/restartScript.py +3 -2
  91. toil/test/server/serverTest.py +1 -1
  92. toil/test/sort/restart_sort.py +2 -1
  93. toil/test/sort/sort.py +2 -1
  94. toil/test/sort/sortTest.py +2 -13
  95. toil/test/src/autoDeploymentTest.py +45 -45
  96. toil/test/src/busTest.py +5 -5
  97. toil/test/src/checkpointTest.py +2 -2
  98. toil/test/src/deferredFunctionTest.py +1 -1
  99. toil/test/src/fileStoreTest.py +32 -16
  100. toil/test/src/helloWorldTest.py +1 -1
  101. toil/test/src/importExportFileTest.py +1 -1
  102. toil/test/src/jobDescriptionTest.py +2 -1
  103. toil/test/src/jobServiceTest.py +1 -1
  104. toil/test/src/jobTest.py +18 -18
  105. toil/test/src/miscTests.py +5 -3
  106. toil/test/src/promisedRequirementTest.py +3 -3
  107. toil/test/src/realtimeLoggerTest.py +1 -1
  108. toil/test/src/resourceTest.py +2 -2
  109. toil/test/src/restartDAGTest.py +1 -1
  110. toil/test/src/resumabilityTest.py +36 -2
  111. toil/test/src/retainTempDirTest.py +1 -1
  112. toil/test/src/systemTest.py +2 -2
  113. toil/test/src/toilContextManagerTest.py +2 -2
  114. toil/test/src/userDefinedJobArgTypeTest.py +1 -1
  115. toil/test/utils/toilDebugTest.py +98 -32
  116. toil/test/utils/toilKillTest.py +2 -2
  117. toil/test/utils/utilsTest.py +20 -0
  118. toil/test/wdl/wdltoil_test.py +148 -45
  119. toil/toilState.py +7 -6
  120. toil/utils/toilClean.py +1 -1
  121. toil/utils/toilConfig.py +36 -0
  122. toil/utils/toilDebugFile.py +60 -33
  123. toil/utils/toilDebugJob.py +39 -12
  124. toil/utils/toilDestroyCluster.py +1 -1
  125. toil/utils/toilKill.py +1 -1
  126. toil/utils/toilLaunchCluster.py +13 -2
  127. toil/utils/toilMain.py +3 -2
  128. toil/utils/toilRsyncCluster.py +1 -1
  129. toil/utils/toilSshCluster.py +1 -1
  130. toil/utils/toilStats.py +240 -143
  131. toil/utils/toilStatus.py +1 -4
  132. toil/version.py +11 -11
  133. toil/wdl/utils.py +2 -122
  134. toil/wdl/wdltoil.py +999 -386
  135. toil/worker.py +25 -31
  136. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/METADATA +60 -53
  137. toil-6.1.0a1.dist-info/RECORD +237 -0
  138. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/WHEEL +1 -1
  139. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/entry_points.txt +0 -1
  140. toil/batchSystems/parasol.py +0 -379
  141. toil/batchSystems/tes.py +0 -459
  142. toil/test/batchSystems/parasolTestSupport.py +0 -117
  143. toil/test/wdl/builtinTest.py +0 -506
  144. toil/test/wdl/conftest.py +0 -23
  145. toil/test/wdl/toilwdlTest.py +0 -522
  146. toil/wdl/toilwdl.py +0 -141
  147. toil/wdl/versions/dev.py +0 -107
  148. toil/wdl/versions/draft2.py +0 -980
  149. toil/wdl/versions/v1.py +0 -794
  150. toil/wdl/wdl_analysis.py +0 -116
  151. toil/wdl/wdl_functions.py +0 -997
  152. toil/wdl/wdl_synthesis.py +0 -1011
  153. toil/wdl/wdl_types.py +0 -243
  154. toil-5.12.0.dist-info/RECORD +0 -244
  155. /toil/{wdl/versions → options}/__init__.py +0 -0
  156. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/LICENSE +0 -0
  157. {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
- """ Return the total printed length of this category item.
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 ) -> int:
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('%s %s %d' % (c, f, self.getWidth(c, f)))
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
- """ Given input t as seconds, return a nicely formatted string.
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((t - (w * 7. * 24. * 60. * 60.)
117
- - (d * 24. * 60. * 60.))
118
- / 60. / 60.)
119
- m = floor((t - (w * 7. * 24. * 60. * 60.)
120
- - (d * 24. * 60. * 60.)
121
- - (h * 60. * 60.)) / 60.)
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(k: float, options: Namespace, field: Optional[int] = None, isBytes: bool = False) -> str:
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(key: str, tag: Expando, options: Namespace, columnWidths: Optional[ColumnWidths] = None) -> str:
157
- """ Generate a pretty-print ready string from a JTTag().
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("Worker Jobs", "min",
167
- "med", "ave", "max")
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 [tag.min_number_per_worker, tag.median_number_per_worker,
170
- tag.average_number_per_worker, tag.max_number_per_worker]:
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 " % (columnWidths.title("time"),
175
- decorateTitle("Time", options))
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 " % (columnWidths.title("clock"),
188
- decorateTitle("Clock", options))
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 " % (columnWidths.title("wait"),
201
- decorateTitle("Wait", options))
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 " % (columnWidths.title("memory"),
214
- decorateTitle("Memory", options))
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
- """ Add a marker to TITLE if the TITLE is sorted on.
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
- def decorateSubHeader(title: str, columnWidths: ColumnWidths, options: Namespace) -> str:
239
- """ Add a marker to the correct field if the TITLE is sorted on.
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"), "min",
245
- columnWidths.getWidth(title, "med"), "med",
246
- columnWidths.getWidth(title, "ave"), "ave",
247
- columnWidths.getWidth(title, "max"), "max",
248
- columnWidths.getWidth(title, "total"), "total")
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 [("min", columnWidths.getWidth(title, "min")),
253
- ("med", columnWidths.getWidth(title, "med")),
254
- ("ave", columnWidths.getWidth(title, "ave")),
255
- ("max", columnWidths.getWidth(title, "max")),
256
- ("total", columnWidths.getWidth(title, "total"))]:
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 = {"med": "median",
276
- "ave": "average",
277
- "min": "min",
278
- "total": "total",
279
- "max": "max",}
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 (options.sortCategory == "time" or
282
- options.sortCategory == "clock" or
283
- options.sortCategory == "wait" or
284
- options.sortCategory == "memory"
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
- # due to https://github.com/python/mypy/issues/9656
289
- key=lambda tag: getattr(tag, "%s_%s" # type: ignore
290
- % (sortField, options.sortCategory)),
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, key=lambda tag: tag.name, # type: ignore
295
- reverse=options.sortReverse)
328
+ jobTypes,
329
+ key=lambda tag: tag.name, # type: ignore
330
+ reverse=options.sortReverse,
331
+ )
296
332
  elif options.sortCategory == "count":
297
- return sorted(jobTypes, key=lambda tag: tag.total_number, # type: ignore
298
- reverse=options.sortReverse)
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(root: Expando, worker: List[Job], job: List[Job], job_types: List[Any], options: Namespace) -> str:
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 += ("Default Cores: %s Default Memory: %s\n"
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 += ("Total Clock: {} Total Runtime: {}\n".format(
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(job_types: List[Any], worker: List[Job], job: List[Job], options: Expando) -> ColumnWidths:
331
- """ Return a ColumnWidths() object with the correct max widths.
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
- """ Update the column width attributes for this tag's fields.
343
- """
344
- longforms = {"med": "median",
345
- "ave": "average",
346
- "min": "min",
347
- "total": "total",
348
- "max": "max",}
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(t, options,
355
- field=cw.getWidth(category, field)).strip()
400
+ s = reportTime(
401
+ t, options, field=cw.getWidth(category, field)
402
+ ).strip()
356
403
  else:
357
- s = reportMemory(t, options,
358
- field=cw.getWidth(category, field), isBytes=True).strip()
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
- """ Create an element for output.
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(float(item.get("requested_cores", 0)), "requested_cores")
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(element: Expando, containingItems: List[Job], containingItemName: str, getFn: Callable[[Job], List[Optional[Job]]]) -> None:
433
- itemCounts = [len(getFn(containingItem)) for
434
- containingItem in containingItems]
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[len(itemCounts) // 2]
439
- element["average_number_per_%s" % containingItemName] = float(sum(itemCounts) / len(itemCounts))
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
- """ Collect and return the stats and config data.
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("File %s contains corrupted json. Skipping file." % fileHandle)
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 'total_time' not in stats or 'total_clock' not in stats:
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(total_run_time=stats.total_time,
478
- total_clock=stats.total_clock,
479
- batch_system=config.batchSystem,
480
- default_memory=str(config.defaultMemory),
481
- default_cores=str(config.defaultCores),
482
- max_cores=str(config.maxCores)
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, 'workers', []) if _f]
487
- jobs = [_f for _f in getattr(stats, 'jobs', []) if _f]
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(buildElement(collatedStatsTag, jobs, "jobs"),
498
- getattr(stats, 'workers', []), "worker", fn4)
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 = [ job for job in jobs if job.class_name == jobName ]
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(tree, tree.worker, tree.jobs, tree.job_types.values(), options)
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 = ['min', 'med', 'ave', 'max', 'total']
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("--outputFile", dest="outputFile", default=None, help="File in which to write results.")
532
- parser.add_argument("--raw", action="store_true", default=False, help="Return raw json data.")
533
- parser.add_argument("--pretty", "--human", action="store_true", default=False,
534
- help="if not raw, prettify the numbers to be human readable.")
535
- parser.add_argument("--sortReverse", "--reverseSort", default=False, action="store_true", help="Reverse sort.")
536
- parser.add_argument("--categories", default=','.join(category_choices), type=str,
537
- help=f"Comma separated list of any of the following: {category_choices}.")
538
- parser.add_argument("--sortCategory", default="time", choices=sort_category_choices,
539
- help=f"How to sort job categories. Choices: {sort_category_choices}. Default: time.")
540
- parser.add_argument("--sortField", default="med", choices=sort_field_choices,
541
- help=f"How to sort job fields. Choices: {sort_field_choices}. Default: med.")
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'{c} not in {category_choices}!')
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)