vortex-nwp 2.0.0b1__py3-none-any.whl → 2.0.0b2__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 (139) hide show
  1. vortex/__init__.py +59 -45
  2. vortex/algo/__init__.py +3 -2
  3. vortex/algo/components.py +940 -614
  4. vortex/algo/mpitools.py +802 -497
  5. vortex/algo/serversynctools.py +34 -33
  6. vortex/config.py +19 -22
  7. vortex/data/__init__.py +9 -3
  8. vortex/data/abstractstores.py +593 -655
  9. vortex/data/containers.py +217 -162
  10. vortex/data/contents.py +65 -39
  11. vortex/data/executables.py +93 -102
  12. vortex/data/flow.py +40 -34
  13. vortex/data/geometries.py +228 -132
  14. vortex/data/handlers.py +428 -225
  15. vortex/data/outflow.py +15 -15
  16. vortex/data/providers.py +185 -163
  17. vortex/data/resources.py +48 -42
  18. vortex/data/stores.py +544 -413
  19. vortex/gloves.py +114 -87
  20. vortex/layout/__init__.py +1 -8
  21. vortex/layout/contexts.py +150 -84
  22. vortex/layout/dataflow.py +353 -202
  23. vortex/layout/monitor.py +264 -128
  24. vortex/nwp/__init__.py +5 -2
  25. vortex/nwp/algo/__init__.py +14 -5
  26. vortex/nwp/algo/assim.py +205 -151
  27. vortex/nwp/algo/clim.py +683 -517
  28. vortex/nwp/algo/coupling.py +447 -225
  29. vortex/nwp/algo/eda.py +437 -229
  30. vortex/nwp/algo/eps.py +403 -231
  31. vortex/nwp/algo/forecasts.py +420 -271
  32. vortex/nwp/algo/fpserver.py +683 -307
  33. vortex/nwp/algo/ifsnaming.py +205 -145
  34. vortex/nwp/algo/ifsroot.py +210 -122
  35. vortex/nwp/algo/monitoring.py +132 -76
  36. vortex/nwp/algo/mpitools.py +321 -191
  37. vortex/nwp/algo/odbtools.py +617 -353
  38. vortex/nwp/algo/oopsroot.py +449 -273
  39. vortex/nwp/algo/oopstests.py +90 -56
  40. vortex/nwp/algo/request.py +287 -206
  41. vortex/nwp/algo/stdpost.py +878 -522
  42. vortex/nwp/data/__init__.py +22 -4
  43. vortex/nwp/data/assim.py +125 -137
  44. vortex/nwp/data/boundaries.py +121 -68
  45. vortex/nwp/data/climfiles.py +193 -211
  46. vortex/nwp/data/configfiles.py +73 -69
  47. vortex/nwp/data/consts.py +426 -401
  48. vortex/nwp/data/ctpini.py +59 -43
  49. vortex/nwp/data/diagnostics.py +94 -66
  50. vortex/nwp/data/eda.py +50 -51
  51. vortex/nwp/data/eps.py +195 -146
  52. vortex/nwp/data/executables.py +440 -434
  53. vortex/nwp/data/fields.py +63 -48
  54. vortex/nwp/data/gridfiles.py +183 -111
  55. vortex/nwp/data/logs.py +250 -217
  56. vortex/nwp/data/modelstates.py +180 -151
  57. vortex/nwp/data/monitoring.py +72 -99
  58. vortex/nwp/data/namelists.py +254 -202
  59. vortex/nwp/data/obs.py +400 -308
  60. vortex/nwp/data/oopsexec.py +22 -20
  61. vortex/nwp/data/providers.py +90 -65
  62. vortex/nwp/data/query.py +71 -82
  63. vortex/nwp/data/stores.py +49 -36
  64. vortex/nwp/data/surfex.py +136 -137
  65. vortex/nwp/syntax/__init__.py +1 -1
  66. vortex/nwp/syntax/stdattrs.py +173 -111
  67. vortex/nwp/tools/__init__.py +2 -2
  68. vortex/nwp/tools/addons.py +22 -17
  69. vortex/nwp/tools/agt.py +24 -12
  70. vortex/nwp/tools/bdap.py +16 -5
  71. vortex/nwp/tools/bdcp.py +4 -1
  72. vortex/nwp/tools/bdm.py +3 -0
  73. vortex/nwp/tools/bdmp.py +14 -9
  74. vortex/nwp/tools/conftools.py +728 -378
  75. vortex/nwp/tools/drhook.py +12 -8
  76. vortex/nwp/tools/grib.py +65 -39
  77. vortex/nwp/tools/gribdiff.py +22 -17
  78. vortex/nwp/tools/ifstools.py +82 -42
  79. vortex/nwp/tools/igastuff.py +167 -143
  80. vortex/nwp/tools/mars.py +14 -2
  81. vortex/nwp/tools/odb.py +234 -125
  82. vortex/nwp/tools/partitioning.py +61 -37
  83. vortex/nwp/tools/satrad.py +27 -12
  84. vortex/nwp/util/async.py +83 -55
  85. vortex/nwp/util/beacon.py +10 -10
  86. vortex/nwp/util/diffpygram.py +174 -86
  87. vortex/nwp/util/ens.py +144 -63
  88. vortex/nwp/util/hooks.py +30 -19
  89. vortex/nwp/util/taskdeco.py +28 -24
  90. vortex/nwp/util/usepygram.py +278 -172
  91. vortex/nwp/util/usetnt.py +31 -17
  92. vortex/sessions.py +72 -39
  93. vortex/syntax/__init__.py +1 -1
  94. vortex/syntax/stdattrs.py +410 -171
  95. vortex/syntax/stddeco.py +31 -22
  96. vortex/toolbox.py +327 -192
  97. vortex/tools/__init__.py +11 -2
  98. vortex/tools/actions.py +125 -59
  99. vortex/tools/addons.py +111 -92
  100. vortex/tools/arm.py +42 -22
  101. vortex/tools/compression.py +72 -69
  102. vortex/tools/date.py +11 -4
  103. vortex/tools/delayedactions.py +242 -132
  104. vortex/tools/env.py +75 -47
  105. vortex/tools/folder.py +342 -171
  106. vortex/tools/grib.py +311 -149
  107. vortex/tools/lfi.py +423 -216
  108. vortex/tools/listings.py +109 -40
  109. vortex/tools/names.py +218 -156
  110. vortex/tools/net.py +632 -298
  111. vortex/tools/parallelism.py +93 -61
  112. vortex/tools/prestaging.py +55 -31
  113. vortex/tools/schedulers.py +172 -105
  114. vortex/tools/services.py +402 -333
  115. vortex/tools/storage.py +293 -358
  116. vortex/tools/surfex.py +24 -24
  117. vortex/tools/systems.py +1211 -631
  118. vortex/tools/targets.py +156 -100
  119. vortex/util/__init__.py +1 -1
  120. vortex/util/config.py +377 -327
  121. vortex/util/empty.py +2 -2
  122. vortex/util/helpers.py +56 -24
  123. vortex/util/introspection.py +18 -12
  124. vortex/util/iosponge.py +8 -4
  125. vortex/util/roles.py +4 -6
  126. vortex/util/storefunctions.py +39 -13
  127. vortex/util/structs.py +3 -3
  128. vortex/util/worker.py +29 -17
  129. vortex_nwp-2.0.0b2.dist-info/METADATA +66 -0
  130. vortex_nwp-2.0.0b2.dist-info/RECORD +142 -0
  131. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/WHEEL +1 -1
  132. vortex/layout/appconf.py +0 -109
  133. vortex/layout/jobs.py +0 -1276
  134. vortex/layout/nodes.py +0 -1424
  135. vortex/layout/subjobs.py +0 -464
  136. vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
  137. vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
  138. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/LICENSE +0 -0
  139. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/top_level.txt +0 -0
vortex/layout/monitor.py CHANGED
@@ -26,24 +26,32 @@ __all__ = []
26
26
 
27
27
 
28
28
  #: Class for possible states of a :class:`InputMonitorEntry` object
29
- EntryStateTuple = namedtuple('EntryStateTuple',
30
- ['ufo', 'expected', 'available', 'failed'])
29
+ EntryStateTuple = namedtuple(
30
+ "EntryStateTuple", ["ufo", "expected", "available", "failed"]
31
+ )
31
32
 
32
33
  #: Predefined :class:`InputMonitorEntry` state values
33
- EntrySt = EntryStateTuple(ufo='ufo', expected='expected', available='available',
34
- failed='failed')
34
+ EntrySt = EntryStateTuple(
35
+ ufo="ufo", expected="expected", available="available", failed="failed"
36
+ )
35
37
 
36
38
  #: Class for possible states of a :class:`_Gang` object
37
- GangStateTuple = namedtuple('GangStateTuple',
38
- ['ufo', 'collectable', 'pcollectable', 'failed'])
39
+ GangStateTuple = namedtuple(
40
+ "GangStateTuple", ["ufo", "collectable", "pcollectable", "failed"]
41
+ )
39
42
 
40
43
  #: Predefined :class:`_Gang` state values
41
- GangSt = GangStateTuple(ufo='undecided', collectable='collectable',
42
- pcollectable='collectable_partial', failed='failed')
44
+ GangSt = GangStateTuple(
45
+ ufo="undecided",
46
+ collectable="collectable",
47
+ pcollectable="collectable_partial",
48
+ failed="failed",
49
+ )
43
50
 
44
51
 
45
52
  class LayoutMonitorError(Exception):
46
53
  """The default exception for this module."""
54
+
47
55
  pass
48
56
 
49
57
 
@@ -74,8 +82,9 @@ class _StateFull:
74
82
  previous = self._state
75
83
  self._state = newstate
76
84
  self._state_changed(previous, self._state)
77
- self._obsboard.notify_upd(self, dict(state=self._state,
78
- previous_state=previous))
85
+ self._obsboard.notify_upd(
86
+ self, dict(state=self._state, previous_state=previous)
87
+ )
79
88
 
80
89
  state = property(_get_state, _set_state, doc="The entry's state.")
81
90
 
@@ -84,7 +93,7 @@ class _StateFullMembersList:
84
93
  """Defines an abstract interface: a class with members."""
85
94
 
86
95
  _mstates = EntrySt # The name of possible member's states
87
- _mcontainer = set # The container class for the members
96
+ _mcontainer = set # The container class for the members
88
97
 
89
98
  def __init__(self):
90
99
  """Initialise the members list."""
@@ -116,7 +125,6 @@ class _StateFullMembersList:
116
125
 
117
126
 
118
127
  class InputMonitorEntry(_StateFull):
119
-
120
128
  def __init__(self, section):
121
129
  """An entry manipulated by a :class:`BasicInputMonitor` object.
122
130
 
@@ -150,9 +158,14 @@ class _MonitorSilencer(ParallelSilencer):
150
158
 
151
159
  def export_result(self, key, ts, prevstate, state):
152
160
  """Returns the recorded data, plus state related informations."""
153
- return dict(report=super().export_result(),
154
- name="Input #{!s}".format(key), key=key,
155
- prevstate=prevstate, state=state, timestamp=ts)
161
+ return dict(
162
+ report=super().export_result(),
163
+ name="Input #{!s}".format(key),
164
+ key=key,
165
+ prevstate=prevstate,
166
+ state=state,
167
+ timestamp=ts,
168
+ )
156
169
 
157
170
 
158
171
  class ManualInputMonitor(_StateFullMembersList):
@@ -164,8 +177,14 @@ class ManualInputMonitor(_StateFullMembersList):
164
177
 
165
178
  _mcontainer = OrderedDict
166
179
 
167
- def __init__(self, context, targets, caching_freq=20, crawling_threshold=100,
168
- mute=False):
180
+ def __init__(
181
+ self,
182
+ context,
183
+ targets,
184
+ caching_freq=20,
185
+ crawling_threshold=100,
186
+ mute=False,
187
+ ):
169
188
  """
170
189
  If the list of inputs is too long (see the *crawling_threshold*
171
190
  option), not all of the inputs will be checked at once: The first
@@ -211,22 +230,24 @@ class ManualInputMonitor(_StateFullMembersList):
211
230
  has_term = 0
212
231
  map_term = defaultdict(int)
213
232
  for e in toclassify:
214
- if hasattr(e.section.rh.resource, 'term'):
233
+ if hasattr(e.section.rh.resource, "term"):
215
234
  has_term += 1
216
235
  map_term[e.section.rh.resource.term.fmthm] += 1
217
236
  if toclassify and has_term == len(toclassify):
218
237
  toclassify.sort(key=lambda e: e.section.rh.resource.term)
219
238
  # Use a crawling threshold that is large enough to span a little bit
220
239
  # more than one term.
221
- self._crawling_threshold = max(self._crawling_threshold,
222
- int(max(map_term.values()) * 1.25))
240
+ self._crawling_threshold = max(
241
+ self._crawling_threshold, int(max(map_term.values()) * 1.25)
242
+ )
223
243
 
224
244
  # Create key/value pairs
225
245
  toclassify = [(i, e) for i, e in enumerate(toclassify)]
226
246
 
227
247
  # Classify the input depending on there stage
228
- self._map_stages = dict(expected=EntrySt.expected,
229
- get=EntrySt.available)
248
+ self._map_stages = dict(
249
+ expected=EntrySt.expected, get=EntrySt.available
250
+ )
230
251
  while toclassify:
231
252
  e = toclassify.pop(0)
232
253
  self._append_entry(self._find_state(e[1], onfails=EntrySt.ufo), e)
@@ -234,9 +255,9 @@ class ManualInputMonitor(_StateFullMembersList):
234
255
  def start(self):
235
256
  """Start the background updater task."""
236
257
  self._mpjob = multiprocessing.Process(
237
- name='BackgroundUpdater',
258
+ name="BackgroundUpdater",
238
259
  target=self._background_updater_job,
239
- args=()
260
+ args=(),
240
261
  )
241
262
  self._mpjob.start()
242
263
 
@@ -249,18 +270,20 @@ class ManualInputMonitor(_StateFullMembersList):
249
270
  t0 = date.now()
250
271
  self._mpjob.join(5)
251
272
  waiting = date.now() - t0
252
- logger.info('Waiting for the background process to stop took %f seconds',
253
- waiting.total_seconds())
273
+ logger.info(
274
+ "Waiting for the background process to stop took %f seconds",
275
+ waiting.total_seconds(),
276
+ )
254
277
  # Be less nice if needed...
255
278
  if self._mpjob.is_alive():
256
- logger.warning('Force termination of the background process')
279
+ logger.warning("Force termination of the background process")
257
280
  self._mpjob.terminate()
258
281
  time.sleep(1) # Allow some time for the process to terminate
259
282
  # Wrap up
260
283
  rc = not self._mperror.is_set()
261
- logger.info('Server still alive ? %s', str(self._mpjob.is_alive()))
284
+ logger.info("Server still alive ? %s", str(self._mpjob.is_alive()))
262
285
  if not rc:
263
- raise LayoutMonitorError('The background process ended badly.')
286
+ raise LayoutMonitorError("The background process ended badly.")
264
287
 
265
288
  def __enter__(self):
266
289
  self.start()
@@ -288,11 +311,11 @@ class ManualInputMonitor(_StateFullMembersList):
288
311
 
289
312
  def _key_update(self, res):
290
313
  """Process a result dictionary of the _background_updater method."""
291
- e = self._members[res['prevstate']].pop(res['key'], None)
314
+ e = self._members[res["prevstate"]].pop(res["key"], None)
292
315
  # The entry might be missing if someone mess with the _memebers dicitonary
293
316
  if e is not None:
294
- self._append_entry(res['state'], (res['key'], e))
295
- self._inactive_since = res['timestamp']
317
+ self._append_entry(res["state"], (res["key"], e))
318
+ self._inactive_since = res["timestamp"]
296
319
 
297
320
  def _background_updater(self):
298
321
  """This method loops on itself regularly to update the entry's state."""
@@ -302,14 +325,17 @@ class ManualInputMonitor(_StateFullMembersList):
302
325
  kangaroo_idx = 0
303
326
 
304
327
  # Stop if we are asked to or if there is nothing more to do
305
- while (not self._mpquit.is_set() and
306
- not (len(self._members[EntrySt.expected]) == 0 and
307
- len(self._members[EntrySt.ufo]) == 0)):
308
-
328
+ while not self._mpquit.is_set() and not (
329
+ len(self._members[EntrySt.expected]) == 0
330
+ and len(self._members[EntrySt.ufo]) == 0
331
+ ):
309
332
  # Tweak the caching_frequency
310
- if (len(self._members[EntrySt.ufo]) and
311
- len(self._members[EntrySt.expected]) <= self._crawling_threshold and
312
- not len(self._members[EntrySt.available])):
333
+ if (
334
+ len(self._members[EntrySt.ufo])
335
+ and len(self._members[EntrySt.expected])
336
+ <= self._crawling_threshold
337
+ and not len(self._members[EntrySt.available])
338
+ ):
313
339
  # If UFO are still there and not much resources are expected,
314
340
  # decrease the caching time
315
341
  eff_caching_freq = max(3, self._caching_freq / 5)
@@ -319,37 +345,63 @@ class ManualInputMonitor(_StateFullMembersList):
319
345
  curtime = time.time()
320
346
  # Crawl into the monitored input if sensible
321
347
  if curtime > last_refresh + eff_caching_freq:
322
-
323
348
  last_refresh = curtime
324
349
  result_stack = list()
325
350
 
326
351
  # Crawl into the ufo list
327
352
  # Always process the first self._crawling_threshold elements
328
- for k, e in islice(self._members[EntrySt.ufo].items(),
329
- self._crawling_threshold):
353
+ for k, e in islice(
354
+ self._members[EntrySt.ufo].items(),
355
+ self._crawling_threshold,
356
+ ):
330
357
  if self._mpquit.is_set(): # Are we ordered to stop ?
331
358
  break
332
- with _MonitorSilencer(self._ctx, 'inputmonitor_updater') as psi:
333
- logger.info("First get on local file: %s",
334
- e.section.rh.container.localpath())
335
- e.section.get(incache=True, fatal=False) # Do not crash at this stage
336
- res = psi.export_result(k, curtime, e.state, self._find_state(e))
359
+ with _MonitorSilencer(
360
+ self._ctx, "inputmonitor_updater"
361
+ ) as psi:
362
+ logger.info(
363
+ "First get on local file: %s",
364
+ e.section.rh.container.localpath(),
365
+ )
366
+ e.section.get(
367
+ incache=True, fatal=False
368
+ ) # Do not crash at this stage
369
+ res = psi.export_result(
370
+ k, curtime, e.state, self._find_state(e)
371
+ )
337
372
  self._mpqueue.put_nowait(res)
338
373
  result_stack.append(res)
339
374
 
340
375
  # What are the expected elements we will look for ?
341
376
  # 1. The first self._crawling_threshold elements
342
- exp_compress = [1, ] * min(self._crawling_threshold,
343
- len(self._members[EntrySt.expected]))
377
+ exp_compress = [
378
+ 1,
379
+ ] * min(
380
+ self._crawling_threshold,
381
+ len(self._members[EntrySt.expected]),
382
+ )
344
383
  # 2. An additional set of self._crawling_threshold rotating elements
345
- for i in range(max(0, len(self._members[EntrySt.expected]) - self._crawling_threshold)):
384
+ for i in range(
385
+ max(
386
+ 0,
387
+ len(self._members[EntrySt.expected])
388
+ - self._crawling_threshold,
389
+ )
390
+ ):
346
391
  kdiff = i - kangaroo_idx
347
- exp_compress.append(1 if kdiff >= 0 and kdiff < self._crawling_threshold else 0)
392
+ exp_compress.append(
393
+ 1
394
+ if kdiff >= 0 and kdiff < self._crawling_threshold
395
+ else 0
396
+ )
348
397
 
349
398
  # Crawl into the chosen items of the expected list
350
399
  (visited, found, kangaroo_incr) = (0, 0, 0)
351
- for i, (k, e) in enumerate(compress(self._members[EntrySt.expected].items(),
352
- exp_compress)):
400
+ for i, (k, e) in enumerate(
401
+ compress(
402
+ self._members[EntrySt.expected].items(), exp_compress
403
+ )
404
+ ):
353
405
  if self._mpquit.is_set(): # Are we ordered to stop ?
354
406
  break
355
407
 
@@ -360,31 +412,49 @@ class ManualInputMonitor(_StateFullMembersList):
360
412
  # If a lot of resources were already found, avoid harassment
361
413
  break
362
414
 
363
- logger.debug("Checking local file: %s (kangaroo=%s)",
364
- e.section.rh.container.localpath(), kangaroo)
415
+ logger.debug(
416
+ "Checking local file: %s (kangaroo=%s)",
417
+ e.section.rh.container.localpath(),
418
+ kangaroo,
419
+ )
365
420
  e.check_done()
366
421
  # Is the promise file still there or not ?
367
422
  if e.section.rh.is_grabable():
368
423
  visited += 1
369
- with _MonitorSilencer(self._ctx, 'inputmonitor_updater') as psi:
424
+ with _MonitorSilencer(
425
+ self._ctx, "inputmonitor_updater"
426
+ ) as psi:
370
427
  if e.section.rh.is_grabable(check_exists=True):
371
- logger.info("The local resource %s becomes available",
372
- e.section.rh.container.localpath())
428
+ logger.info(
429
+ "The local resource %s becomes available",
430
+ e.section.rh.container.localpath(),
431
+ )
373
432
  # This will crash in case of an error, but this should
374
433
  # not happen since we checked the resource just above
375
434
  e.section.get(incache=True)
376
435
  found += 1
377
- res = psi.export_result(k, curtime, e.state, self._find_state(e))
436
+ res = psi.export_result(
437
+ k, curtime, e.state, self._find_state(e)
438
+ )
378
439
  else:
379
- logger.warning("The local resource %s has failed",
380
- e.section.rh.container.localpath())
381
- res = psi.export_result(k, curtime, e.state, EntrySt.failed)
440
+ logger.warning(
441
+ "The local resource %s has failed",
442
+ e.section.rh.container.localpath(),
443
+ )
444
+ res = psi.export_result(
445
+ k, curtime, e.state, EntrySt.failed
446
+ )
382
447
  self._mpqueue.put_nowait(res)
383
448
  result_stack.append(res)
384
449
 
385
450
  # Update the kangaroo index
386
451
  kangaroo_idx = kangaroo_idx + kangaroo_incr - visited
387
- if kangaroo_idx > len(self._members[EntrySt.expected]) - self._crawling_threshold - 1:
452
+ if (
453
+ kangaroo_idx
454
+ > len(self._members[EntrySt.expected])
455
+ - self._crawling_threshold
456
+ - 1
457
+ ):
388
458
  kangaroo_idx = 0
389
459
 
390
460
  # Effectively update the internal _members dictionary
@@ -401,9 +471,9 @@ class ManualInputMonitor(_StateFullMembersList):
401
471
  self._background_updater()
402
472
  except Exception:
403
473
  (exc_type, exc_value, exc_traceback) = sys.exc_info()
404
- print('Exception type: {!s}'.format(exc_type))
405
- print('Exception info: {!s}'.format(exc_value))
406
- print('Traceback:')
474
+ print("Exception type: {!s}".format(exc_type))
475
+ print("Exception info: {!s}".format(exc_value))
476
+ print("Traceback:")
407
477
  print("\n".join(traceback.format_tb(exc_traceback)))
408
478
  # Alert the main process of the error
409
479
  self._mperror.set()
@@ -415,7 +485,7 @@ class ManualInputMonitor(_StateFullMembersList):
415
485
  # That's bad...
416
486
  if self._mperror.is_set():
417
487
  self.stop()
418
- raise LayoutMonitorError('The background process ended badly.')
488
+ raise LayoutMonitorError("The background process ended badly.")
419
489
  # Process all the available update messages
420
490
  while True:
421
491
  try:
@@ -425,8 +495,9 @@ class ManualInputMonitor(_StateFullMembersList):
425
495
  if prp is None:
426
496
  prp = ParallelResultParser(self._ctx)
427
497
  if not self._mute:
428
- self._ctx.system.highlight("The InputMonitor got news for: {!s}"
429
- .format(r['name']))
498
+ self._ctx.system.highlight(
499
+ "The InputMonitor got news for: {!s}".format(r["name"])
500
+ )
430
501
  prp(r)
431
502
  print()
432
503
  self._key_update(r)
@@ -435,8 +506,10 @@ class ManualInputMonitor(_StateFullMembersList):
435
506
  def all_done(self):
436
507
  """Are there any ufo or expected sections left ?"""
437
508
  self._refresh()
438
- return (len(self._members[EntrySt.expected]) == 0 and
439
- len(self._members[EntrySt.ufo]) == 0)
509
+ return (
510
+ len(self._members[EntrySt.expected]) == 0
511
+ and len(self._members[EntrySt.ufo]) == 0
512
+ )
440
513
 
441
514
  @property
442
515
  def inactive_time(self):
@@ -479,9 +552,13 @@ class ManualInputMonitor(_StateFullMembersList):
479
552
  time_now = time.time()
480
553
  if time_now - self._last_healthcheck > interval:
481
554
  self._last_healthcheck = time_now
482
- logger.info("Still waiting (ufo=%d, expected=%d, available=%d, failed=%d)...",
483
- len(self._members[EntrySt.ufo]), len(self._members[EntrySt.expected]),
484
- len(self._members[EntrySt.available]), len(self._members[EntrySt.failed]))
555
+ logger.info(
556
+ "Still waiting (ufo=%d, expected=%d, available=%d, failed=%d)...",
557
+ len(self._members[EntrySt.ufo]),
558
+ len(self._members[EntrySt.expected]),
559
+ len(self._members[EntrySt.available]),
560
+ len(self._members[EntrySt.failed]),
561
+ )
485
562
 
486
563
  def is_timedout(self, timeout, exception=None):
487
564
  """Check if a timeout occurred.
@@ -493,9 +570,15 @@ class ManualInputMonitor(_StateFullMembersList):
493
570
  self._refresh()
494
571
  if (timeout > 0) and (self.inactive_time > timeout):
495
572
  logger.error("The waiting loop timed out (%d seconds)", timeout)
496
- logger.error("The following files are still unaccounted for: %s",
497
- ",".join([e.section.rh.container.localpath()
498
- for e in self.expected.values()]))
573
+ logger.error(
574
+ "The following files are still unaccounted for: %s",
575
+ ",".join(
576
+ [
577
+ e.section.rh.container.localpath()
578
+ for e in self.expected.values()
579
+ ]
580
+ ),
581
+ )
499
582
  rc = True
500
583
  if rc and exception is not None:
501
584
  raise exception("The waiting loop timed-out")
@@ -511,8 +594,15 @@ class BasicInputMonitor(ManualInputMonitor):
511
594
 
512
595
  _mcontainer = OrderedDict
513
596
 
514
- def __init__(self, context, role=None, kind=None,
515
- caching_freq=20, crawling_threshold=100, mute=False):
597
+ def __init__(
598
+ self,
599
+ context,
600
+ role=None,
601
+ kind=None,
602
+ caching_freq=20,
603
+ crawling_threshold=100,
604
+ mute=False,
605
+ ):
516
606
  """
517
607
  If the list of inputs is too long (see the *crawling_threshold*
518
608
  option), not all of the inputs will be checked at once: The first
@@ -540,13 +630,19 @@ class BasicInputMonitor(ManualInputMonitor):
540
630
  self._role = role
541
631
  self._kind = kind
542
632
  assert not (self._role is None and self._kind is None)
543
- ManualInputMonitor.__init__(self, context,
544
- [InputMonitorEntry(x)
545
- for x in context.sequence.filtered_inputs(role=self._role,
546
- kind=self._kind)],
547
- caching_freq=caching_freq,
548
- crawling_threshold=crawling_threshold,
549
- mute=mute)
633
+ ManualInputMonitor.__init__(
634
+ self,
635
+ context,
636
+ [
637
+ InputMonitorEntry(x)
638
+ for x in context.sequence.filtered_inputs(
639
+ role=self._role, kind=self._kind
640
+ )
641
+ ],
642
+ caching_freq=caching_freq,
643
+ crawling_threshold=crawling_threshold,
644
+ mute=mute,
645
+ )
550
646
 
551
647
 
552
648
  class _Gang(observer.Observer, _StateFull, _StateFullMembersList):
@@ -580,10 +676,11 @@ class _Gang(observer.Observer, _StateFull, _StateFullMembersList):
580
676
  def nickname(self):
581
677
  """A fancy representation of the Gang's motive."""
582
678
  if not self.info:
583
- return 'Anonymous'
679
+ return "Anonymous"
584
680
  else:
585
- return ", ".join(['{:s}={!s}'.format(k, v)
586
- for k, v in self.info.items()])
681
+ return ", ".join(
682
+ ["{:s}={!s}".format(k, v) for k, v in self.info.items()]
683
+ )
587
684
 
588
685
  def add_member(self, *members):
589
686
  """Introduce one or several members to the Gang."""
@@ -603,8 +700,8 @@ class _Gang(observer.Observer, _StateFull, _StateFullMembersList):
603
700
  with self._t_lock:
604
701
  observer.Observer.updobsitem(self, item, info)
605
702
  # Move the item around
606
- self._members[info['previous_state']].remove(item)
607
- self._members[info['state']].add(item)
703
+ self._members[info["previous_state"]].remove(item)
704
+ self._members[info["state"]].add(item)
608
705
  # Update my own state
609
706
  self._refresh_state()
610
707
 
@@ -665,20 +762,29 @@ class BasicGang(_Gang):
665
762
  # Remove the waitlimit timer
666
763
  if self._waitlimit_timer is not None and not self._ufo_members:
667
764
  self._waitlimit_timer.cancel()
668
- logger.debug('Waitlimit Timer thread canceled: %s (Gang: %s)',
669
- self._waitlimit_timer, self.nickname)
765
+ logger.debug(
766
+ "Waitlimit Timer thread canceled: %s (Gang: %s)",
767
+ self._waitlimit_timer,
768
+ self.nickname,
769
+ )
670
770
  self._waitlimit_timer = None
671
771
  # Print some diagnosis data
672
772
  if self.info and new != self._mystates.ufo:
673
- msg = ("State changed from {:s} to {:s} for Gang: {:s}"
674
- .format(previous, new, self.nickname))
773
+ msg = "State changed from {:s} to {:s} for Gang: {:s}".format(
774
+ previous, new, self.nickname
775
+ )
675
776
  if new == self._mystates.pcollectable:
676
777
  if self._ufo_members:
677
- logger.warning("%s\nSome of the Gang's members are still expected " +
678
- "but the %d seconds waitlimit is exhausted.",
679
- msg, self.waitlimit)
778
+ logger.warning(
779
+ "%s\nSome of the Gang's members are still expected "
780
+ + "but the %d seconds waitlimit is exhausted.",
781
+ msg,
782
+ self.waitlimit,
783
+ )
680
784
  else:
681
- logger.warning("%s\nSome of the Gang's members have failed.", msg)
785
+ logger.warning(
786
+ "%s\nSome of the Gang's members have failed.", msg
787
+ )
682
788
  else:
683
789
  logger.info(msg)
684
790
 
@@ -688,30 +794,40 @@ class BasicGang(_Gang):
688
794
  def _waitlimit_check():
689
795
  with self._t_lock:
690
796
  self._refresh_state()
691
- logger.debug('Waitlimit Timer thread done: %s (Gang: %s)',
692
- self._waitlimit_timer, self.nickname)
797
+ logger.debug(
798
+ "Waitlimit Timer thread done: %s (Gang: %s)",
799
+ self._waitlimit_timer,
800
+ self.nickname,
801
+ )
693
802
  self._waitlimit_timer = None
694
803
 
695
- self._waitlimit_timer = threading.Timer(self.waitlimit + 1,
696
- _waitlimit_check)
804
+ self._waitlimit_timer = threading.Timer(
805
+ self.waitlimit + 1, _waitlimit_check
806
+ )
697
807
  self._waitlimit_timer.daemon = True
698
808
  self._waitlimit_timer.start()
699
- logger.debug('Waitlimit Timer thread started: %s (Gang: %s)',
700
- self._waitlimit_timer, self.nickname)
809
+ logger.debug(
810
+ "Waitlimit Timer thread started: %s (Gang: %s)",
811
+ self._waitlimit_timer,
812
+ self.nickname,
813
+ )
701
814
 
702
815
  def add_member(self, *members):
703
816
  with self._t_lock:
704
817
  super().add_member(*members)
705
- if self._firstseen is None and any([m.state == self._mstates.available
706
- for m in members]):
818
+ if self._firstseen is None and any(
819
+ [m.state == self._mstates.available for m in members]
820
+ ):
707
821
  self._firstseen = time.time()
708
822
  self._set_waitlimit_timer()
709
823
 
710
824
  def updobsitem(self, item, info):
711
825
  with self._t_lock:
712
826
  super().updobsitem(item, info)
713
- if (self._firstseen is None and
714
- info['state'] == self._mstates.available):
827
+ if (
828
+ self._firstseen is None
829
+ and info["state"] == self._mstates.available
830
+ ):
715
831
  self._firstseen = time.time()
716
832
  self._set_waitlimit_timer()
717
833
 
@@ -723,21 +839,29 @@ class BasicGang(_Gang):
723
839
  @property
724
840
  def _ufo_members(self):
725
841
  """The number of ufo members (from a Gang point of view)."""
726
- return len(self._members[self._mstates.ufo]) + len(self._members[self._mstates.expected])
842
+ return len(self._members[self._mstates.ufo]) + len(
843
+ self._members[self._mstates.expected]
844
+ )
727
845
 
728
846
  def _is_collectable(self):
729
847
  return len(self._members[self._mstates.available]) == len(self)
730
848
 
731
849
  def _is_pcollectable(self):
732
- return (len(self._members[self._mstates.available]) >= self._eff_minsize and
733
- (self._ufo_members == 0 or
734
- (self._firstseen is not None and
735
- time.time() - self._firstseen > self.waitlimit > 0)
736
- )
737
- )
850
+ return len(
851
+ self._members[self._mstates.available]
852
+ ) >= self._eff_minsize and (
853
+ self._ufo_members == 0
854
+ or (
855
+ self._firstseen is not None
856
+ and time.time() - self._firstseen > self.waitlimit > 0
857
+ )
858
+ )
738
859
 
739
860
  def _is_undecided(self):
740
- return len(self._members[self._mstates.available]) + self._ufo_members >= self._eff_minsize
861
+ return (
862
+ len(self._members[self._mstates.available]) + self._ufo_members
863
+ >= self._eff_minsize
864
+ )
741
865
 
742
866
 
743
867
  class MetaGang(_Gang):
@@ -764,19 +888,24 @@ class MetaGang(_Gang):
764
888
 
765
889
  def has_pcollectable(self):
766
890
  """Is there at least one collectable or collectable_partial member ?"""
767
- return (len(self._members[self._mstates.pcollectable]) +
768
- len(self._members[self._mstates.collectable]))
891
+ return len(self._members[self._mstates.pcollectable]) + len(
892
+ self._members[self._mstates.collectable]
893
+ )
769
894
 
770
895
  def pop_collectable(self):
771
896
  """Retrieve a collectable member."""
772
- return self._unregister_i(self._members[self._mstates.collectable].pop())
897
+ return self._unregister_i(
898
+ self._members[self._mstates.collectable].pop()
899
+ )
773
900
 
774
901
  def pop_pcollectable(self):
775
902
  """Retrieve a collectable or a collectable_partial member."""
776
903
  if self.has_collectable():
777
904
  return self.pop_collectable()
778
905
  else:
779
- return self._unregister_i(self._members[self._mstates.pcollectable].pop())
906
+ return self._unregister_i(
907
+ self._members[self._mstates.pcollectable].pop()
908
+ )
780
909
 
781
910
  def consume_colectable(self):
782
911
  """Retriece all collectable members (as a generator)."""
@@ -792,8 +921,10 @@ class MetaGang(_Gang):
792
921
  return len(self._members[self._mstates.collectable]) == len(self)
793
922
 
794
923
  def _is_pcollectable(self):
795
- return (len(self._members[self._mstates.collectable]) +
796
- len(self._members[self._mstates.pcollectable])) == len(self)
924
+ return (
925
+ len(self._members[self._mstates.collectable])
926
+ + len(self._members[self._mstates.pcollectable])
927
+ ) == len(self)
797
928
 
798
929
  def _is_undecided(self):
799
930
  return len(self._members[self._mstates.failed]) == 0
@@ -821,13 +952,18 @@ class AutoMetaGang(MetaGang):
821
952
  # Initialise the gangs
822
953
  mdict = defaultdict(list)
823
954
  for entry in bm.memberslist:
824
- entryid = tuple([entry.section.rh.wide_key_lookup(key)
825
- for key in grouping_keys])
955
+ entryid = tuple(
956
+ [
957
+ entry.section.rh.wide_key_lookup(key)
958
+ for key in grouping_keys
959
+ ]
960
+ )
826
961
  mdict[entryid].append(entry)
827
962
  # Finalise the Gangs setup and use them...
828
963
  for entryid, members in mdict.items():
829
- gang = BasicGang(waitlimit=waitlimit,
830
- minsize=len(members) - allowmissing)
831
- gang.add_member(* members)
964
+ gang = BasicGang(
965
+ waitlimit=waitlimit, minsize=len(members) - allowmissing
966
+ )
967
+ gang.add_member(*members)
832
968
  gang.info = {k: v for k, v in zip(grouping_keys, entryid)}
833
969
  self.add_member(gang)