vortex-nwp 2.0.0b1__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. vortex/__init__.py +75 -47
  2. vortex/algo/__init__.py +3 -2
  3. vortex/algo/components.py +944 -618
  4. vortex/algo/mpitools.py +802 -497
  5. vortex/algo/mpitools_templates/__init__.py +1 -0
  6. vortex/algo/serversynctools.py +34 -33
  7. vortex/config.py +19 -22
  8. vortex/data/__init__.py +9 -3
  9. vortex/data/abstractstores.py +593 -655
  10. vortex/data/containers.py +217 -162
  11. vortex/data/contents.py +65 -39
  12. vortex/data/executables.py +93 -102
  13. vortex/data/flow.py +40 -34
  14. vortex/data/geometries.py +228 -132
  15. vortex/data/handlers.py +436 -227
  16. vortex/data/outflow.py +15 -15
  17. vortex/data/providers.py +185 -163
  18. vortex/data/resources.py +48 -42
  19. vortex/data/stores.py +540 -417
  20. vortex/data/sync_templates/__init__.py +0 -0
  21. vortex/gloves.py +114 -87
  22. vortex/layout/__init__.py +1 -8
  23. vortex/layout/contexts.py +150 -84
  24. vortex/layout/dataflow.py +353 -202
  25. vortex/layout/monitor.py +264 -128
  26. vortex/nwp/__init__.py +5 -2
  27. vortex/nwp/algo/__init__.py +14 -5
  28. vortex/nwp/algo/assim.py +205 -151
  29. vortex/nwp/algo/clim.py +683 -517
  30. vortex/nwp/algo/coupling.py +447 -225
  31. vortex/nwp/algo/eda.py +437 -229
  32. vortex/nwp/algo/eps.py +403 -231
  33. vortex/nwp/algo/forecasts.py +416 -275
  34. vortex/nwp/algo/fpserver.py +683 -307
  35. vortex/nwp/algo/ifsnaming.py +205 -145
  36. vortex/nwp/algo/ifsroot.py +215 -122
  37. vortex/nwp/algo/monitoring.py +137 -76
  38. vortex/nwp/algo/mpitools.py +330 -190
  39. vortex/nwp/algo/odbtools.py +637 -353
  40. vortex/nwp/algo/oopsroot.py +454 -273
  41. vortex/nwp/algo/oopstests.py +90 -56
  42. vortex/nwp/algo/request.py +287 -206
  43. vortex/nwp/algo/stdpost.py +878 -522
  44. vortex/nwp/data/__init__.py +22 -4
  45. vortex/nwp/data/assim.py +125 -137
  46. vortex/nwp/data/boundaries.py +121 -68
  47. vortex/nwp/data/climfiles.py +193 -211
  48. vortex/nwp/data/configfiles.py +73 -69
  49. vortex/nwp/data/consts.py +426 -401
  50. vortex/nwp/data/ctpini.py +59 -43
  51. vortex/nwp/data/diagnostics.py +94 -66
  52. vortex/nwp/data/eda.py +50 -51
  53. vortex/nwp/data/eps.py +195 -146
  54. vortex/nwp/data/executables.py +440 -434
  55. vortex/nwp/data/fields.py +63 -48
  56. vortex/nwp/data/gridfiles.py +183 -111
  57. vortex/nwp/data/logs.py +250 -217
  58. vortex/nwp/data/modelstates.py +180 -151
  59. vortex/nwp/data/monitoring.py +72 -99
  60. vortex/nwp/data/namelists.py +254 -202
  61. vortex/nwp/data/obs.py +400 -308
  62. vortex/nwp/data/oopsexec.py +22 -20
  63. vortex/nwp/data/providers.py +90 -65
  64. vortex/nwp/data/query.py +71 -82
  65. vortex/nwp/data/stores.py +49 -36
  66. vortex/nwp/data/surfex.py +136 -137
  67. vortex/nwp/syntax/__init__.py +1 -1
  68. vortex/nwp/syntax/stdattrs.py +173 -111
  69. vortex/nwp/tools/__init__.py +2 -2
  70. vortex/nwp/tools/addons.py +22 -17
  71. vortex/nwp/tools/agt.py +24 -12
  72. vortex/nwp/tools/bdap.py +16 -5
  73. vortex/nwp/tools/bdcp.py +4 -1
  74. vortex/nwp/tools/bdm.py +3 -0
  75. vortex/nwp/tools/bdmp.py +14 -9
  76. vortex/nwp/tools/conftools.py +728 -378
  77. vortex/nwp/tools/drhook.py +12 -8
  78. vortex/nwp/tools/grib.py +65 -39
  79. vortex/nwp/tools/gribdiff.py +22 -17
  80. vortex/nwp/tools/ifstools.py +82 -42
  81. vortex/nwp/tools/igastuff.py +167 -143
  82. vortex/nwp/tools/mars.py +14 -2
  83. vortex/nwp/tools/odb.py +234 -125
  84. vortex/nwp/tools/partitioning.py +61 -37
  85. vortex/nwp/tools/satrad.py +27 -12
  86. vortex/nwp/util/async.py +83 -55
  87. vortex/nwp/util/beacon.py +10 -10
  88. vortex/nwp/util/diffpygram.py +174 -86
  89. vortex/nwp/util/ens.py +144 -63
  90. vortex/nwp/util/hooks.py +30 -19
  91. vortex/nwp/util/taskdeco.py +28 -24
  92. vortex/nwp/util/usepygram.py +278 -172
  93. vortex/nwp/util/usetnt.py +31 -17
  94. vortex/sessions.py +72 -39
  95. vortex/syntax/__init__.py +1 -1
  96. vortex/syntax/stdattrs.py +410 -171
  97. vortex/syntax/stddeco.py +31 -22
  98. vortex/toolbox.py +327 -192
  99. vortex/tools/__init__.py +11 -2
  100. vortex/tools/actions.py +110 -121
  101. vortex/tools/addons.py +111 -92
  102. vortex/tools/arm.py +42 -22
  103. vortex/tools/compression.py +72 -69
  104. vortex/tools/date.py +11 -4
  105. vortex/tools/delayedactions.py +242 -132
  106. vortex/tools/env.py +75 -47
  107. vortex/tools/folder.py +342 -171
  108. vortex/tools/grib.py +341 -162
  109. vortex/tools/lfi.py +423 -216
  110. vortex/tools/listings.py +109 -40
  111. vortex/tools/names.py +218 -156
  112. vortex/tools/net.py +655 -299
  113. vortex/tools/parallelism.py +93 -61
  114. vortex/tools/prestaging.py +55 -31
  115. vortex/tools/schedulers.py +172 -105
  116. vortex/tools/services.py +403 -334
  117. vortex/tools/storage.py +293 -358
  118. vortex/tools/surfex.py +24 -24
  119. vortex/tools/systems.py +1234 -643
  120. vortex/tools/targets.py +156 -100
  121. vortex/util/__init__.py +1 -1
  122. vortex/util/config.py +378 -327
  123. vortex/util/empty.py +2 -2
  124. vortex/util/helpers.py +56 -24
  125. vortex/util/introspection.py +18 -12
  126. vortex/util/iosponge.py +8 -4
  127. vortex/util/roles.py +4 -6
  128. vortex/util/storefunctions.py +39 -13
  129. vortex/util/structs.py +3 -3
  130. vortex/util/worker.py +29 -17
  131. vortex_nwp-2.1.0.dist-info/METADATA +67 -0
  132. vortex_nwp-2.1.0.dist-info/RECORD +144 -0
  133. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/WHEEL +1 -1
  134. vortex/layout/appconf.py +0 -109
  135. vortex/layout/jobs.py +0 -1276
  136. vortex/layout/nodes.py +0 -1424
  137. vortex/layout/subjobs.py +0 -464
  138. vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
  139. vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
  140. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info/licenses}/LICENSE +0 -0
  141. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/top_level.txt +0 -0
vortex/layout/dataflow.py CHANGED
@@ -23,28 +23,29 @@ __all__ = []
23
23
 
24
24
  logger = loggers.getLogger(__name__)
25
25
 
26
- _RHANDLERS_OBSBOARD = 'Resources-Handlers'
26
+ _RHANDLERS_OBSBOARD = "Resources-Handlers"
27
27
 
28
28
 
29
29
  class SectionFatalError(Exception):
30
30
  """Exception when fatal mode is activated."""
31
+
31
32
  pass
32
33
 
33
34
 
34
35
  #: Definition of a named tuple INTENT
35
- IntentTuple = namedtuple('IntentTuple', ['IN', 'OUT', 'INOUT'])
36
+ IntentTuple = namedtuple("IntentTuple", ["IN", "OUT", "INOUT"])
36
37
 
37
38
  #: Predefined INTENT values IN, OUT and INOUT.
38
- intent = IntentTuple(IN='in', OUT='out', INOUT='inout')
39
+ intent = IntentTuple(IN="in", OUT="out", INOUT="inout")
39
40
 
40
41
  #: Definition of a named tuple IXO sequence
41
- IXOTuple = namedtuple('IXOTuple', ['INPUT', 'OUTPUT', 'EXEC'])
42
+ IXOTuple = namedtuple("IXOTuple", ["INPUT", "OUTPUT", "EXEC"])
42
43
 
43
44
  #: Predefined IXO sequence values INPUT, OUTPUT and EXEC.
44
45
  ixo = IXOTuple(INPUT=1, OUTPUT=2, EXEC=3)
45
46
 
46
47
  #: Arguments specific to a section (to be striped away from a resource handler description)
47
- section_args = ['role', 'alternate', 'intent', 'fatal', 'coherentgroup']
48
+ section_args = ["role", "alternate", "intent", "fatal", "coherentgroup"]
48
49
 
49
50
 
50
51
  def stripargs_section(**kw):
@@ -64,22 +65,28 @@ class _ReplaceSectionArgs:
64
65
  Trigger the footprint's replacement mechanism on some of the section arguments.
65
66
  """
66
67
 
67
- _REPL_TODO = ('coherentgroup', )
68
+ _REPL_TODO = ("coherentgroup",)
68
69
 
69
70
  def __init__(self):
70
- self._fptmp = footprints.Footprint(attr={k: dict(optional=True)
71
- for k in self._REPL_TODO})
71
+ self._fptmp = footprints.Footprint(
72
+ attr={k: dict(optional=True) for k in self._REPL_TODO}
73
+ )
72
74
 
73
75
  def __call__(self, rh, opts):
74
- if any({footprints.replattr.search(opts[k])
75
- for k in self._REPL_TODO if k in opts}):
76
+ if any(
77
+ {
78
+ footprints.replattr.search(opts[k])
79
+ for k in self._REPL_TODO
80
+ if k in opts
81
+ }
82
+ ):
76
83
  # The "description"
77
84
  desc = opts.copy()
78
85
  if rh is not None:
79
86
  desc.update(rh.options)
80
- desc['container'] = rh.container
81
- desc['provider'] = rh.provider
82
- desc['resource'] = rh.resource
87
+ desc["container"] = rh.container
88
+ desc["provider"] = rh.provider
89
+ desc["resource"] = rh.resource
83
90
  # Resolve
84
91
  resolved, _, _ = self._fptmp.resolve(desc, fatal=False, fast=False)
85
92
  # ok, let's use the resolved values
@@ -95,25 +102,28 @@ class Section:
95
102
  """Low level unit to handle a resource."""
96
103
 
97
104
  def __init__(self, **kw):
98
- logger.debug('Section initialisation %s', self)
105
+ logger.debug("Section initialisation %s", self)
99
106
  self.kind = ixo.INPUT
100
107
  self.intent = intent.INOUT
101
108
  self.fatal = True
102
109
  # Fetch the ResourceHandler
103
- self._rh = kw.pop('rh', None)
110
+ self._rh = kw.pop("rh", None)
104
111
  # We realy need a ResourceHandler...
105
112
  if self.rh is None:
106
113
  raise AttributeError("A proper rh attribute have to be provided")
107
114
  # Call the footprint's replacement mechanism if needed
108
115
  _default_replace_section_args(self._rh, kw)
109
116
  # Process the remaining options
110
- self._role = setrole(kw.pop('role', 'anonymous'))
111
- self._alternate = setrole(kw.pop('alternate', None))
112
- self._coherentgroups = kw.pop('coherentgroup', None)
113
- self._coherentgroups = set(self._coherentgroups.split(',')
114
- if self._coherentgroups else [])
117
+ self._role = setrole(kw.pop("role", "anonymous"))
118
+ self._alternate = setrole(kw.pop("alternate", None))
119
+ self._coherentgroups = kw.pop("coherentgroup", None)
120
+ self._coherentgroups = set(
121
+ self._coherentgroups.split(",") if self._coherentgroups else []
122
+ )
115
123
  self._coherentgroups_opened = {g: True for g in self._coherentgroups}
116
- self.stages = [kw.pop('stage', 'load'), ]
124
+ self.stages = [
125
+ kw.pop("stage", "load"),
126
+ ]
117
127
  self.__dict__.update(kw)
118
128
  # If alternate is specified role have to be removed
119
129
  if self._alternate:
@@ -135,20 +145,28 @@ class Section:
135
145
  @property
136
146
  def any_coherentgroup_opened(self):
137
147
  """Is, at least, one belonging coherent group opened ?"""
138
- return not self.coherentgroups or any(self._coherentgroups_opened.values())
148
+ return not self.coherentgroups or any(
149
+ self._coherentgroups_opened.values()
150
+ )
139
151
 
140
152
  def coherent_group_close(self, group):
141
153
  """Close the coherent group (get and put will fail from now and on)."""
142
154
  if group in self._coherentgroups_opened:
143
155
  self._coherentgroups_opened[group] = False
144
156
  # Another group's resource failed, re-checking and possibly deleting myself !
145
- if self.stage in ('expected', 'get') and not self.any_coherentgroup_opened:
146
- logger.info('Clearing %s because of the coherent group failure.', str(self.rh.container))
157
+ if (
158
+ self.stage in ("expected", "get")
159
+ and not self.any_coherentgroup_opened
160
+ ):
161
+ logger.info(
162
+ "Clearing %s because of the coherent group failure.",
163
+ str(self.rh.container),
164
+ )
147
165
  self.rh.clear()
148
166
 
149
167
  def check_groupstatus(self, info):
150
168
  """Given the updstage's info dict, check that a coherent group still holds"""
151
- return info.get('stage') != 'void'
169
+ return info.get("stage") != "void"
152
170
 
153
171
  @property
154
172
  def rh(self):
@@ -161,41 +179,49 @@ class Section:
161
179
 
162
180
  def _updignore(self, info):
163
181
  """Fake function for undefined information driven updates."""
164
- logger.warning('Unable to update %s with info %s', self, info)
182
+ logger.warning("Unable to update %s with info %s", self, info)
165
183
 
166
184
  def _updstage_void(self, info):
167
185
  """Upgrade current section to 'checked' level."""
168
- if info.get('stage') == 'void' and self.kind in (ixo.INPUT, ixo.EXEC):
169
- self.stages.append('void')
186
+ if info.get("stage") == "void" and self.kind in (ixo.INPUT, ixo.EXEC):
187
+ self.stages.append("void")
170
188
 
171
189
  def _updstage_checked(self, info):
172
190
  """Upgrade current section to 'checked' level."""
173
- if info.get('stage') == 'checked' and self.kind in (ixo.INPUT, ixo.EXEC):
174
- self.stages.append('checked')
191
+ if info.get("stage") == "checked" and self.kind in (
192
+ ixo.INPUT,
193
+ ixo.EXEC,
194
+ ):
195
+ self.stages.append("checked")
175
196
 
176
197
  def _updstage_get(self, info):
177
198
  """Upgrade current section to 'get' level."""
178
- if info.get('stage') == 'get' and self.kind in (ixo.INPUT, ixo.EXEC):
179
- self.stages.append('get')
199
+ if info.get("stage") == "get" and self.kind in (ixo.INPUT, ixo.EXEC):
200
+ self.stages.append("get")
180
201
 
181
202
  def _updstage_expected(self, info):
182
203
  """Upgrade current section to 'expected' level."""
183
- if info.get('stage') == 'expected' and self.kind in (ixo.INPUT, ixo.EXEC):
184
- self.stages.append('expected')
204
+ if info.get("stage") == "expected" and self.kind in (
205
+ ixo.INPUT,
206
+ ixo.EXEC,
207
+ ):
208
+ self.stages.append("expected")
185
209
 
186
210
  def _updstage_put(self, info):
187
211
  """Upgrade current section to 'put' level."""
188
- if info.get('stage') == 'put' and self.kind == ixo.OUTPUT:
189
- self.stages.append('put')
212
+ if info.get("stage") == "put" and self.kind == ixo.OUTPUT:
213
+ self.stages.append("put")
190
214
 
191
215
  def _updstage_ghost(self, info):
192
216
  """Upgrade current section to 'ghost' level."""
193
- if info.get('stage') == 'ghost' and self.kind == ixo.OUTPUT:
194
- self.stages.append('ghost')
217
+ if info.get("stage") == "ghost" and self.kind == ixo.OUTPUT:
218
+ self.stages.append("ghost")
195
219
 
196
220
  def updstage(self, info):
197
221
  """Upgrade current section level according to information given in dict ``info``."""
198
- updmethod = getattr(self, '_updstage_' + info.get('stage'), self._updignore)
222
+ updmethod = getattr(
223
+ self, "_updstage_" + info.get("stage"), self._updignore
224
+ )
199
225
  updmethod(info)
200
226
 
201
227
  def _stronglocate(self, **kw):
@@ -203,60 +229,76 @@ class Section:
203
229
  try:
204
230
  loc = self.rh.locate(**kw)
205
231
  except Exception:
206
- loc = '???'
232
+ loc = "???"
207
233
  return loc
208
234
 
209
235
  def _fatal_wrap(self, sectiontype, callback, **kw):
210
236
  """Launch **callback** and process the returncode/exceptions according to **fatal**."""
211
- action = {'input': 'get', 'output': 'put'}[sectiontype]
237
+ action = {"input": "get", "output": "put"}[sectiontype]
212
238
  rc = False
213
239
  try:
214
240
  rc = callback(**kw)
215
241
  except Exception as e:
216
- logger.error('Something wrong (%s section): %s. %s',
217
- sectiontype, str(e), traceback.format_exc())
218
- logger.error('Resource %s', self._stronglocate())
242
+ logger.error(
243
+ "Something wrong (%s section): %s. %s",
244
+ sectiontype,
245
+ str(e),
246
+ traceback.format_exc(),
247
+ )
248
+ logger.error("Resource %s", self._stronglocate())
219
249
  if not rc and self.fatal:
220
- logger.critical('Fatal error with action %s on %s', action, self._stronglocate())
221
- raise SectionFatalError('Could not {:s} resource {!s}'.format(action, rc))
250
+ logger.critical(
251
+ "Fatal error with action %s on %s",
252
+ action,
253
+ self._stronglocate(),
254
+ )
255
+ raise SectionFatalError(
256
+ "Could not {:s} resource {!s}".format(action, rc)
257
+ )
222
258
  return rc
223
259
 
224
260
  def _just_fail(self, sectiontype, **kw): # @UnusedVariable
225
261
  """Check if a resource exists but fails anyway."""
226
- action = {'input': 'get', 'output': 'put'}[sectiontype]
262
+ action = {"input": "get", "output": "put"}[sectiontype]
227
263
  rc = False
228
264
  if self.fatal:
229
- logger.critical('Fatal error with action %s on %s', action, self._stronglocate())
230
- raise SectionFatalError('Could not {:s} resource {!s}'.format(action, rc))
265
+ logger.critical(
266
+ "Fatal error with action %s on %s",
267
+ action,
268
+ self._stronglocate(),
269
+ )
270
+ raise SectionFatalError(
271
+ "Could not {:s} resource {!s}".format(action, rc)
272
+ )
231
273
  return rc
232
274
 
233
275
  def get(self, **kw):
234
276
  """Shortcut to resource handler :meth:`~vortex.data.handlers.get`."""
235
277
  if self.kind == ixo.INPUT or self.kind == ixo.EXEC:
236
278
  if self.any_coherentgroup_opened:
237
- kw['intent'] = self.intent
279
+ kw["intent"] = self.intent
238
280
  if self.alternate:
239
- kw['alternate'] = self.alternate
240
- rc = self._fatal_wrap('input', self.rh.get, **kw)
281
+ kw["alternate"] = self.alternate
282
+ rc = self._fatal_wrap("input", self.rh.get, **kw)
241
283
  else:
242
284
  logger.info("The coherent group is closed... doing nothing.")
243
- rc = self._just_fail('input')
285
+ rc = self._just_fail("input")
244
286
  else:
245
287
  rc = False
246
- logger.error('Try to get from an output section')
288
+ logger.error("Try to get from an output section")
247
289
  return rc
248
290
 
249
291
  def finaliseget(self):
250
292
  """Shortcut to resource handler :meth:`~vortex.data.handlers.finaliseget`."""
251
293
  if self.kind == ixo.INPUT or self.kind == ixo.EXEC:
252
294
  if self.any_coherentgroup_opened:
253
- rc = self._fatal_wrap('input', self.rh.finaliseget)
295
+ rc = self._fatal_wrap("input", self.rh.finaliseget)
254
296
  else:
255
297
  logger.info("The coherent group is closed... doing nothing.")
256
- rc = self._just_fail('input')
298
+ rc = self._just_fail("input")
257
299
  else:
258
300
  rc = False
259
- logger.error('Try to get from an output section')
301
+ logger.error("Try to get from an output section")
260
302
  return rc
261
303
 
262
304
  def earlyget(self, **kw):
@@ -264,10 +306,10 @@ class Section:
264
306
  rc = False
265
307
  if self.kind == ixo.INPUT or self.kind == ixo.EXEC:
266
308
  if self.any_coherentgroup_opened:
267
- kw['intent'] = self.intent
309
+ kw["intent"] = self.intent
268
310
  if self.alternate:
269
- kw['alternate'] = self.alternate
270
- rc = self.rh.earlyget(** kw)
311
+ kw["alternate"] = self.alternate
312
+ rc = self.rh.earlyget(**kw)
271
313
  else:
272
314
  rc = None
273
315
  return rc
@@ -276,24 +318,29 @@ class Section:
276
318
  """Shortcut to resource handler :meth:`~vortex.data.handlers.put`."""
277
319
  if self.kind == ixo.OUTPUT:
278
320
  if self.any_coherentgroup_opened:
279
- kw['intent'] = self.intent
280
- rc = self._fatal_wrap('output', self.rh.put, **kw)
321
+ kw["intent"] = self.intent
322
+ rc = self._fatal_wrap("output", self.rh.put, **kw)
281
323
  else:
282
324
  logger.info("The coherent group is closed... failing !.")
283
325
  rc = False
284
326
  if self.fatal:
285
- logger.critical('Fatal error with action put on %s', self._stronglocate())
286
- raise SectionFatalError('Could not get resource {!s}'.format(rc))
327
+ logger.critical(
328
+ "Fatal error with action put on %s",
329
+ self._stronglocate(),
330
+ )
331
+ raise SectionFatalError(
332
+ "Could not get resource {!s}".format(rc)
333
+ )
287
334
  else:
288
335
  rc = False
289
- logger.error('Try to put from an input section.')
336
+ logger.error("Try to put from an input section.")
290
337
  return rc
291
338
 
292
339
  def show(self, **kw):
293
340
  """Nice dump of the section attributes and contents."""
294
341
  for k, v in sorted(vars(self).items()):
295
- if k != 'rh':
296
- print(' ', k.ljust(16), ':', v)
342
+ if k != "rh":
343
+ print(" ", k.ljust(16), ":", v)
297
344
  self.rh.quickview(indent=1)
298
345
 
299
346
  def as_dict(self):
@@ -306,12 +353,12 @@ class Section:
306
353
  outdict["coherentgroup"] = ",".join(sorted(v))
307
354
  elif k == "_coherentgroups_opened":
308
355
  continue
309
- elif k.startswith('_'):
356
+ elif k.startswith("_"):
310
357
  outdict[k[1:]] = v
311
358
  else:
312
359
  outdict[k] = v
313
360
  # Add the latest stage
314
- outdict['stage'] = self.stage
361
+ outdict["stage"] = self.stage
315
362
  return outdict
316
363
 
317
364
 
@@ -322,7 +369,7 @@ class Sequence(observer.Observer):
322
369
  """
323
370
 
324
371
  def __init__(self, *args, **kw):
325
- logger.debug('Sequence initialisation %s', self)
372
+ logger.debug("Sequence initialisation %s", self)
326
373
  self.sections = list()
327
374
  # This hash table will be used to speedup the searches...
328
375
  # If one uses the remove method, a WeakSet is not usefull. However,
@@ -367,7 +414,11 @@ class Sequence(observer.Observer):
367
414
  if not self._coherentgroups_openings[cgroup]:
368
415
  candidate.coherent_group_close(cgroup)
369
416
  else:
370
- logger.warning('Try to add a non-section object %s in sequence %s', candidate, self)
417
+ logger.warning(
418
+ "Try to add a non-section object %s in sequence %s",
419
+ candidate,
420
+ self,
421
+ )
371
422
 
372
423
  def remove(self, candidate):
373
424
  """
@@ -376,59 +427,71 @@ class Sequence(observer.Observer):
376
427
  """
377
428
  if isinstance(candidate, Section):
378
429
  self.sections.remove(candidate)
379
- self._sections_hash[candidate.rh.simplified_hashkey].discard(candidate)
430
+ self._sections_hash[candidate.rh.simplified_hashkey].discard(
431
+ candidate
432
+ )
380
433
  for cgroup in candidate.coherentgroups:
381
434
  self._coherentgroups[cgroup].discard(candidate)
382
435
  else:
383
- logger.warning('Try to remove a non-section object %s in sequence %s', candidate, self)
436
+ logger.warning(
437
+ "Try to remove a non-section object %s in sequence %s",
438
+ candidate,
439
+ self,
440
+ )
384
441
 
385
442
  def section(self, **kw):
386
443
  """Section factory wrapping a given ``rh`` (Resource Handler)."""
387
- rhset = kw.get('rh', list())
444
+ rhset = kw.get("rh", list())
388
445
  if not isinstance(rhset, list):
389
- rhset = [rhset, ]
390
- ralter = kw.get('alternate', kw.get('role', 'anonymous'))
446
+ rhset = [
447
+ rhset,
448
+ ]
449
+ ralter = kw.get("alternate", kw.get("role", "anonymous"))
391
450
  newsections = list()
392
451
  for rh in rhset:
393
- kw['rh'] = rh
452
+ kw["rh"] = rh
394
453
  this_section = Section(**kw)
395
454
  self.add(this_section)
396
455
  newsections.append(this_section)
397
- kw['alternate'] = ralter
398
- if 'role' in kw:
399
- del kw['role']
456
+ kw["alternate"] = ralter
457
+ if "role" in kw:
458
+ del kw["role"]
400
459
  return newsections
401
460
 
402
461
  def input(self, **kw):
403
462
  """Create a section with default kind equal to ``ixo.INPUT``."""
404
- if 'kind' in kw:
405
- del kw['kind']
406
- kw.setdefault('intent', intent.IN)
463
+ if "kind" in kw:
464
+ del kw["kind"]
465
+ kw.setdefault("intent", intent.IN)
407
466
  return self.section(kind=ixo.INPUT, **kw)
408
467
 
409
468
  def output(self, **kw):
410
469
  """Create a section with default kind equal to ``ixo.OUTPUT`` and intent equal to ``intent.OUT``."""
411
- if 'kind' in kw:
412
- del kw['kind']
413
- kw.setdefault('intent', intent.OUT)
470
+ if "kind" in kw:
471
+ del kw["kind"]
472
+ kw.setdefault("intent", intent.OUT)
414
473
  return self.section(kind=ixo.OUTPUT, **kw)
415
474
 
416
475
  def executable(self, **kw):
417
476
  """Create a section with default kind equal to to ``ixo.EXEC``."""
418
- if 'kind' in kw:
419
- del kw['kind']
420
- kw.setdefault('intent', intent.IN)
477
+ if "kind" in kw:
478
+ del kw["kind"]
479
+ kw.setdefault("intent", intent.IN)
421
480
  return self.section(kind=ixo.EXEC, **kw)
422
481
 
423
482
  @staticmethod
424
483
  def _fuzzy_match(stuff, allowed):
425
484
  """Check if ``stuff`` is in ``allowed``. ``allowed`` may contain regex."""
426
- if (isinstance(allowed, str) or
427
- not isinstance(allowed, collections.abc.Iterable)):
428
- allowed = [allowed, ]
485
+ if isinstance(allowed, str) or not isinstance(
486
+ allowed, collections.abc.Iterable
487
+ ):
488
+ allowed = [
489
+ allowed,
490
+ ]
429
491
  for pattern in allowed:
430
- if ((isinstance(pattern, re.Pattern) and pattern.search(stuff)) or
431
- (pattern == stuff)):
492
+ if (isinstance(pattern, re.Pattern) and pattern.search(stuff)) or (
493
+ pattern == stuff
494
+ ):
432
495
  return True
433
496
  return False
434
497
 
@@ -437,18 +500,31 @@ class Sequence(observer.Observer):
437
500
  return list(sections)
438
501
  inrole = list()
439
502
  inkind = list()
440
- with_alternates = not kw.get('no_alternates', False)
441
- if 'role' in kw and kw['role'] is not None:
442
- selectrole = mktuple(kw['role'])
443
- inrole = [x for x in sections if (
444
- (x.role is not None and self._fuzzy_match(x.role, selectrole)) or
445
- (with_alternates and
446
- x.alternate is not None and
447
- self._fuzzy_match(x.alternate, selectrole))
448
- )]
449
- if not inrole and 'kind' in kw:
450
- selectkind = mktuple(kw['kind'])
451
- inkind = [x for x in sections if self._fuzzy_match(x.rh.resource.realkind, selectkind)]
503
+ with_alternates = not kw.get("no_alternates", False)
504
+ if "role" in kw and kw["role"] is not None:
505
+ selectrole = mktuple(kw["role"])
506
+ inrole = [
507
+ x
508
+ for x in sections
509
+ if (
510
+ (
511
+ x.role is not None
512
+ and self._fuzzy_match(x.role, selectrole)
513
+ )
514
+ or (
515
+ with_alternates
516
+ and x.alternate is not None
517
+ and self._fuzzy_match(x.alternate, selectrole)
518
+ )
519
+ )
520
+ ]
521
+ if not inrole and "kind" in kw:
522
+ selectkind = mktuple(kw["kind"])
523
+ inkind = [
524
+ x
525
+ for x in sections
526
+ if self._fuzzy_match(x.rh.resource.realkind, selectkind)
527
+ ]
452
528
  return inrole or inkind
453
529
 
454
530
  def inputs(self):
@@ -472,8 +548,12 @@ class Sequence(observer.Observer):
472
548
  Similar to :meth:`filtered_inputs` but only walk through the inputs of
473
549
  that reached the 'get' or 'expected' stage.
474
550
  """
475
- return [x for x in self._section_list_filter(list(self.inputs()), **kw)
476
- if (x.stage == 'get' or x.stage == 'expected') and x.rh.container.exists()]
551
+ return [
552
+ x
553
+ for x in self._section_list_filter(list(self.inputs()), **kw)
554
+ if (x.stage == "get" or x.stage == "expected")
555
+ and x.rh.container.exists()
556
+ ]
477
557
 
478
558
  def filtered_inputs(self, **kw):
479
559
  """Walk through the inputs of the current sequence.
@@ -493,15 +573,23 @@ class Sequence(observer.Observer):
493
573
  def is_somehow_viable(self, section):
494
574
  """Tells wether *section* is ok or has a viable alternate."""
495
575
  if section.role is None:
496
- raise ValueError('An alternate section was given ; this is incorrect...')
497
- if section.stage in ('get', 'expected') and section.rh.container.exists():
576
+ raise ValueError(
577
+ "An alternate section was given ; this is incorrect..."
578
+ )
579
+ if (
580
+ section.stage in ("get", "expected")
581
+ and section.rh.container.exists()
582
+ ):
498
583
  return section
499
584
  else:
500
585
  for isec in self.inputs():
501
- if (isec.alternate == section.role and
502
- isec.stage in ('get', 'expected') and
503
- isec.rh.container.localpath() == section.rh.container.localpath() and
504
- isec.rh.container.exists()):
586
+ if (
587
+ isec.alternate == section.role
588
+ and isec.stage in ("get", "expected")
589
+ and isec.rh.container.localpath()
590
+ == section.rh.container.localpath()
591
+ and isec.rh.container.exists()
592
+ ):
505
593
  return isec
506
594
  return None
507
595
 
@@ -541,7 +629,9 @@ class Sequence(observer.Observer):
541
629
 
542
630
  for cgroup in a_section.coherentgroups:
543
631
  if self._coherentgroups_openings[cgroup]:
544
- if not all(map(_s_group_check, self.coherentgroup_iter(cgroup))):
632
+ if not all(
633
+ map(_s_group_check, self.coherentgroup_iter(cgroup))
634
+ ):
545
635
  for c_section in self.coherentgroup_iter(cgroup):
546
636
  c_section.coherent_group_close(cgroup)
547
637
  self._coherentgroups_openings[cgroup] = False
@@ -551,13 +641,14 @@ class Sequence(observer.Observer):
551
641
  Resources-Handlers observing facility.
552
642
  Track hashkey alteration for the resource handler ``item``.
553
643
  """
554
- if (info['observerboard'] == _RHANDLERS_OBSBOARD and
555
- 'oldhash' in info):
556
- logger.debug('Notified %s upd item %s', self, item)
557
- oldhash = info['oldhash']
644
+ if info["observerboard"] == _RHANDLERS_OBSBOARD and "oldhash" in info:
645
+ logger.debug("Notified %s upd item %s", self, item)
646
+ oldhash = info["oldhash"]
558
647
  # First remove the oldhash
559
648
  if oldhash in self._sections_hash:
560
- for section in [s for s in self._sections_hash[oldhash] if s.rh is item]:
649
+ for section in [
650
+ s for s in self._sections_hash[oldhash] if s.rh is item
651
+ ]:
561
652
  self._sections_hash[oldhash].discard(section)
562
653
  # Then add the new hash: This is relatively slow so that it should not be used much...
563
654
  for section in [s for s in self.sections if s.rh is item]:
@@ -581,39 +672,55 @@ class Sequence(observer.Observer):
581
672
  elif trydict and isinstance(skeleton, dict):
582
673
  # We assume it is a resource handler dictionary
583
674
  try:
584
- hkey = (skeleton['resource'].get('kind', None),
585
- skeleton['container'].get('filename', None))
675
+ hkey = (
676
+ skeleton["resource"].get("kind", None),
677
+ skeleton["container"].get("filename", None),
678
+ )
586
679
  except KeyError:
587
- logger.critical("This is probably not a ResourceHandler dictionary.")
680
+ logger.critical(
681
+ "This is probably not a ResourceHandler dictionary."
682
+ )
588
683
  raise
589
684
  return self._sections_hash[hkey]
590
- raise ValueError("Cannot process a {!s} type skeleton".format(type(skeleton)))
685
+ raise ValueError(
686
+ "Cannot process a {!s} type skeleton".format(type(skeleton))
687
+ )
591
688
 
592
689
 
593
690
  #: Class of a list of statuses
594
- InputsReportStatusTupple = namedtuple('InputsReportStatusTupple',
595
- ('PRESENT', 'EXPECTED', 'CHECKED', 'MISSING', 'UNUSED'))
691
+ InputsReportStatusTupple = namedtuple(
692
+ "InputsReportStatusTupple",
693
+ ("PRESENT", "EXPECTED", "CHECKED", "MISSING", "UNUSED"),
694
+ )
596
695
 
597
696
 
598
697
  #: Possible statuses used in :class:`SequenceInputsReport` objects
599
- InputsReportStatus = InputsReportStatusTupple(PRESENT='present', EXPECTED='expected',
600
- CHECKED='checked', MISSING='missing',
601
- UNUSED='unused')
698
+ InputsReportStatus = InputsReportStatusTupple(
699
+ PRESENT="present",
700
+ EXPECTED="expected",
701
+ CHECKED="checked",
702
+ MISSING="missing",
703
+ UNUSED="unused",
704
+ )
602
705
 
603
706
 
604
707
  class SequenceInputsReport:
605
708
  """Summarize data about inputs (missing resources, alternates, ...)."""
606
709
 
607
- _TranslateStage = dict(get=InputsReportStatus.PRESENT, expected=InputsReportStatus.EXPECTED,
608
- checked=InputsReportStatus.CHECKED, void=InputsReportStatus.MISSING,
609
- load=InputsReportStatus.UNUSED)
710
+ _TranslateStage = dict(
711
+ get=InputsReportStatus.PRESENT,
712
+ expected=InputsReportStatus.EXPECTED,
713
+ checked=InputsReportStatus.CHECKED,
714
+ void=InputsReportStatus.MISSING,
715
+ load=InputsReportStatus.UNUSED,
716
+ )
610
717
 
611
718
  def __init__(self, inputs):
612
719
  self._local_map = defaultdict(lambda: defaultdict(list))
613
720
  for insec in inputs:
614
721
  local = insec.rh.container.localpath()
615
722
  # Determine if the current section is an alternate or not...
616
- kind = 'alternate' if insec.alternate is not None else 'nominal'
723
+ kind = "alternate" if insec.alternate is not None else "nominal"
617
724
  self._local_map[local][kind].append(insec)
618
725
 
619
726
  def _local_status(self, local):
@@ -627,20 +734,29 @@ class SequenceInputsReport:
627
734
  """
628
735
  desc = self._local_map[local]
629
736
  # First, check the nominal resource
630
- if len(desc['nominal']) > 0:
631
- nominal = desc['nominal'][-1]
737
+ if len(desc["nominal"]) > 0:
738
+ nominal = desc["nominal"][-1]
632
739
  status = self._TranslateStage[nominal.stage]
633
740
  true_rh = nominal.rh
634
741
  else:
635
- logger.warning('No nominal section for < %s >. This should not happened !', local)
742
+ logger.warning(
743
+ "No nominal section for < %s >. This should not happened !",
744
+ local,
745
+ )
636
746
  nominal = None
637
747
  status = None
638
748
  true_rh = None
639
749
  # Look for alternates:
640
- if status not in (InputsReportStatus.PRESENT, InputsReportStatus.EXPECTED):
641
- for alter in desc['alternate']:
750
+ if status not in (
751
+ InputsReportStatus.PRESENT,
752
+ InputsReportStatus.EXPECTED,
753
+ ):
754
+ for alter in desc["alternate"]:
642
755
  alter_status = self._TranslateStage[alter.stage]
643
- if alter_status in (InputsReportStatus.PRESENT, InputsReportStatus.EXPECTED):
756
+ if alter_status in (
757
+ InputsReportStatus.PRESENT,
758
+ InputsReportStatus.EXPECTED,
759
+ ):
644
760
  status = alter_status
645
761
  true_rh = alter.rh
646
762
  break
@@ -662,24 +778,30 @@ class SequenceInputsReport:
662
778
  else:
663
779
  # Convert a single string to a list
664
780
  if isinstance(only, str):
665
- only = [only, ]
781
+ only = [
782
+ only,
783
+ ]
666
784
  # Check that the provided statuses exist
667
785
  if not all([f in InputsReportStatus for f in only]):
668
786
  return "* The only attribute is wrong ! ({!s})".format(only)
669
787
 
670
- outstr = ''
788
+ outstr = ""
671
789
  for local in sorted(self._local_map):
672
790
  # For each and every local file, check alternates and find out the status
673
791
  status, true_rh, nominal_rh = self._local_status(local)
674
- extrainfo = ''
792
+ extrainfo = ""
675
793
  # Detect alternates
676
- is_alternate = status != InputsReportStatus.MISSING and (true_rh is not nominal_rh)
794
+ is_alternate = status != InputsReportStatus.MISSING and (
795
+ true_rh is not nominal_rh
796
+ )
677
797
  if is_alternate:
678
- extrainfo = '(ALTERNATE USED)'
798
+ extrainfo = "(ALTERNATE USED)"
679
799
  # Alternates are always printed. Otherwise rely on **only**
680
800
  if is_alternate or status in only:
681
- outstr += "* {:8s} {:16s} : {:s}\n".format(status, extrainfo, local)
682
- if detailed and extrainfo != '':
801
+ outstr += "* {:8s} {:16s} : {:s}\n".format(
802
+ status, extrainfo, local
803
+ )
804
+ if detailed and extrainfo != "":
683
805
  outstr += " * The following resource is used:\n"
684
806
  outstr += true_rh.idcard(indent=4) + "\n"
685
807
  if nominal_rh is not None:
@@ -712,7 +834,9 @@ class SequenceInputsReport:
712
834
  outstack = dict()
713
835
  for local in self._local_map:
714
836
  status, true_rh, nominal_rh = self._local_status(local)
715
- if status != InputsReportStatus.MISSING and (true_rh is not nominal_rh):
837
+ if status != InputsReportStatus.MISSING and (
838
+ true_rh is not nominal_rh
839
+ ):
716
840
  outstack[local] = (true_rh, nominal_rh)
717
841
  return outstack
718
842
 
@@ -720,8 +844,11 @@ class SequenceInputsReport:
720
844
  """List the missing local resources."""
721
845
  outstack = dict()
722
846
  for local in self._local_map:
723
- (status, true_rh, # @UnusedVariable
724
- nominal_rh) = self._local_status(local)
847
+ (
848
+ status,
849
+ true_rh, # @UnusedVariable
850
+ nominal_rh,
851
+ ) = self._local_status(local)
725
852
  if status == InputsReportStatus.MISSING:
726
853
  outstack[local] = nominal_rh
727
854
  return outstack
@@ -729,15 +856,21 @@ class SequenceInputsReport:
729
856
 
730
857
  def _fast_clean_uri(store, remote):
731
858
  """Clean a URI so that it can be compared with a JSON load version."""
732
- qsl = remote['query'].copy()
733
- qsl.update({'storearg_{:s}'.format(k): v
734
- for k, v in store.tracking_extraargs.items()})
735
- return {'scheme': str(store.scheme),
736
- 'netloc': str(store.netloc),
737
- 'path': str(remote['path']),
738
- 'params': str(remote['params']),
739
- 'query': qsl,
740
- 'fragment': str(remote['fragment'])}
859
+ qsl = remote["query"].copy()
860
+ qsl.update(
861
+ {
862
+ "storearg_{:s}".format(k): v
863
+ for k, v in store.tracking_extraargs.items()
864
+ }
865
+ )
866
+ return {
867
+ "scheme": str(store.scheme),
868
+ "netloc": str(store.netloc),
869
+ "path": str(remote["path"]),
870
+ "params": str(remote["params"]),
871
+ "query": qsl,
872
+ "fragment": str(remote["fragment"]),
873
+ }
741
874
 
742
875
 
743
876
  class LocalTrackerEntry:
@@ -748,8 +881,11 @@ class LocalTrackerEntry:
748
881
  stores are tracked.
749
882
  """
750
883
 
751
- _actions = ('get', 'put',)
752
- _internals = ('rhdict', 'hook', 'uri')
884
+ _actions = (
885
+ "get",
886
+ "put",
887
+ )
888
+ _internals = ("rhdict", "hook", "uri")
753
889
 
754
890
  def __init__(self, master_tracker=None):
755
891
  """
@@ -771,8 +907,8 @@ class LocalTrackerEntry:
771
907
  return json.loads(json.dumps(stuff))
772
908
 
773
909
  def _clean_rhdict(self, rhdict):
774
- if 'options' in rhdict:
775
- del rhdict['options']
910
+ if "options" in rhdict:
911
+ del rhdict["options"]
776
912
  return self._jsonize(rhdict)
777
913
 
778
914
  def update_rh(self, rh, info):
@@ -784,13 +920,15 @@ class LocalTrackerEntry:
784
920
  :param rh: :class:`~vortex.data.handlers.Handler` object that sends the update.
785
921
  :param info: Info dictionary sent by the :class:`~vortex.data.handlers.Handler` object
786
922
  """
787
- stage = info['stage']
923
+ stage = info["stage"]
788
924
  if self._check_action(stage):
789
- if 'hook' in info:
790
- self._data['hook'][stage].append(self._jsonize(info['hook']))
791
- elif not info.get('insitu', False):
925
+ if "hook" in info:
926
+ self._data["hook"][stage].append(self._jsonize(info["hook"]))
927
+ elif not info.get("insitu", False):
792
928
  # We are using as_dict since this may be written to a JSON file
793
- self._data['rhdict'][stage].append(self._clean_rhdict(rh.as_dict()))
929
+ self._data["rhdict"][stage].append(
930
+ self._clean_rhdict(rh.as_dict())
931
+ )
794
932
 
795
933
  def _update_store(self, info, uri):
796
934
  """Update the entry based on data received from the observer board.
@@ -801,10 +939,10 @@ class LocalTrackerEntry:
801
939
  :param info: Info dictionary sent by the :class:`~vortex.data.stores.Store` object
802
940
  :param uri: A cleaned (i.e. compatible with JSON) representation of the URI
803
941
  """
804
- action = info['action']
942
+ action = info["action"]
805
943
  # Only known action and successfull attempts
806
- if self._check_action(action) and info['status']:
807
- self._data['uri'][action].append(uri)
944
+ if self._check_action(action) and info["status"]:
945
+ self._data["uri"][action].append(uri)
808
946
  if self._master_tracker is not None:
809
947
  self._master_tracker.uri_map_append(self, action, uri)
810
948
 
@@ -820,22 +958,24 @@ class LocalTrackerEntry:
820
958
  """
821
959
  self._data = dumpeddict
822
960
  for action in self._actions:
823
- for uri in self._data['uri'][action]:
961
+ for uri in self._data["uri"][action]:
824
962
  self._master_tracker.uri_map_append(self, action, uri)
825
963
 
826
964
  def append(self, anotherentry):
827
965
  """Append the content of another LocalTrackerEntry object into this one."""
828
966
  for internal in self._internals:
829
967
  for act in self._actions:
830
- self._data[internal][act].extend(anotherentry._data[internal][act])
968
+ self._data[internal][act].extend(
969
+ anotherentry._data[internal][act]
970
+ )
831
971
 
832
972
  def latest_rhdict(self, action):
833
973
  """Return the dictionary that represents the latest :class:`~vortex.data.handlers.Handler` object involved.
834
974
 
835
975
  :param action: Action that is considered.
836
976
  """
837
- if self._check_action(action) and self._data['rhdict'][action]:
838
- return self._data['rhdict'][action][-1]
977
+ if self._check_action(action) and self._data["rhdict"][action]:
978
+ return self._data["rhdict"][action][-1]
839
979
  else:
840
980
  return dict()
841
981
 
@@ -853,9 +993,9 @@ class LocalTrackerEntry:
853
993
  for key, item in latest.items():
854
994
  newitem = cleaned.get(key, None)
855
995
  if newitem != item:
856
- logger.error('Expected %s:', key)
996
+ logger.error("Expected %s:", key)
857
997
  logger.error(pprint.pformat(item))
858
- logger.error('Got:')
998
+ logger.error("Got:")
859
999
  logger.error(pprint.pformat(newitem))
860
1000
  return res
861
1001
  else:
@@ -869,10 +1009,10 @@ class LocalTrackerEntry:
869
1009
 
870
1010
  :param uri: A cleaned (i.e. compatible with JSON) representation of the URI
871
1011
  """
872
- while uri in self._data['uri']['put']:
873
- self._data['uri']['put'].remove(uri)
1012
+ while uri in self._data["uri"]["put"]:
1013
+ self._data["uri"]["put"].remove(uri)
874
1014
  if self._master_tracker is not None:
875
- self._master_tracker.uri_map_remove(self, 'put', uri)
1015
+ self._master_tracker.uri_map_remove(self, "put", uri)
876
1016
 
877
1017
  def _redundant_stuff(self, internal, action, stuff):
878
1018
  if self._check_action(action):
@@ -886,7 +1026,7 @@ class LocalTrackerEntry:
886
1026
  :param action: Action that is considered.
887
1027
  :param hookname: Name of the Hook function that will be checked.
888
1028
  """
889
- return self._redundant_stuff('hook', action, self._jsonize(hookname))
1029
+ return self._redundant_stuff("hook", action, self._jsonize(hookname))
890
1030
 
891
1031
  def redundant_uri(self, action, store, remote):
892
1032
  """Check if an URI has already been processed.
@@ -895,7 +1035,9 @@ class LocalTrackerEntry:
895
1035
  :param store: :class:`~vortex.data.stores.Store` object that will be checked.
896
1036
  :param remote: Remote path that will be checked.
897
1037
  """
898
- return self._redundant_stuff('uri', action, _fast_clean_uri(store, remote))
1038
+ return self._redundant_stuff(
1039
+ "uri", action, _fast_clean_uri(store, remote)
1040
+ )
899
1041
 
900
1042
  def _grep_stuff(self, internal, action, skeleton=dict()):
901
1043
  stack = []
@@ -903,20 +1045,24 @@ class LocalTrackerEntry:
903
1045
  if isinstance(element, collections.abc.Mapping):
904
1046
  succeed = True
905
1047
  for key, val in skeleton.items():
906
- succeed = succeed and ((key in element) and (element[key] == val))
1048
+ succeed = succeed and (
1049
+ (key in element) and (element[key] == val)
1050
+ )
907
1051
  if succeed:
908
1052
  stack.append(element)
909
1053
  return stack
910
1054
 
911
1055
  def __str__(self):
912
- out = ''
1056
+ out = ""
913
1057
  for action in self._actions:
914
1058
  for internal in self._internals:
915
1059
  if len(self._data[internal][action]) > 0:
916
1060
  out += "+ {:4s} / {}\n{}\n".format(
917
1061
  action.upper(),
918
1062
  internal,
919
- EncodedPrettyPrinter().pformat(self._data[internal][action])
1063
+ EncodedPrettyPrinter().pformat(
1064
+ self._data[internal][action]
1065
+ ),
920
1066
  )
921
1067
  return out
922
1068
 
@@ -929,7 +1075,7 @@ class LocalTracker(defaultdict):
929
1075
  object.
930
1076
  """
931
1077
 
932
- _default_json_filename = 'local-tracker-state.json'
1078
+ _default_json_filename = "local-tracker-state.json"
933
1079
 
934
1080
  def __init__(self):
935
1081
  super().__init__()
@@ -972,13 +1118,14 @@ class LocalTracker(defaultdict):
972
1118
  """
973
1119
  lpath = rh.container.iotarget()
974
1120
  if isinstance(lpath, str):
975
- if info.get('clear', False):
1121
+ if info.get("clear", False):
976
1122
  self.pop(lpath, None)
977
1123
  else:
978
1124
  self[lpath].update_rh(rh, info)
979
1125
  else:
980
- logger.debug('The iotarget is not a str: skipped in %s',
981
- self.__class__)
1126
+ logger.debug(
1127
+ "The iotarget is not a str: skipped in %s", self.__class__
1128
+ )
982
1129
 
983
1130
  def update_store(self, store, info):
984
1131
  """Update the object based on data received from the observer board.
@@ -989,21 +1136,23 @@ class LocalTracker(defaultdict):
989
1136
  :param store: :class:`~vortex.data.stores.Store` object that sends the update.
990
1137
  :param info: Info dictionary sent by the :class:`~vortex.data.stores.Store` object
991
1138
  """
992
- lpath = info.get('local', None)
1139
+ lpath = info.get("local", None)
993
1140
  if lpath is None:
994
1141
  # Check for file deleted on the remote side
995
- if info['action'] == 'del' and info['status']:
996
- clean_uri = _fast_clean_uri(store, info['remote'])
1142
+ if info["action"] == "del" and info["status"]:
1143
+ clean_uri = _fast_clean_uri(store, info["remote"])
997
1144
  huri = self._hashable_uri(clean_uri)
998
- for atracker in list(self._uri_map['put'][huri]):
1145
+ for atracker in list(self._uri_map["put"][huri]):
999
1146
  atracker._check_uri_remote_delete(clean_uri)
1000
1147
  else:
1001
1148
  if isinstance(lpath, str):
1002
- clean_uri = _fast_clean_uri(store, info['remote'])
1149
+ clean_uri = _fast_clean_uri(store, info["remote"])
1003
1150
  self[lpath]._update_store(info, clean_uri)
1004
1151
  else:
1005
- logger.debug("The iotarget isn't a str: It will be skipped in %s",
1006
- self.__class__)
1152
+ logger.debug(
1153
+ "The iotarget isn't a str: It will be skipped in %s",
1154
+ self.__class__,
1155
+ )
1007
1156
 
1008
1157
  def is_tracked_input(self, local):
1009
1158
  """Check if the given `local` container is listed as an input and associated with
@@ -1011,9 +1160,11 @@ class LocalTracker(defaultdict):
1011
1160
 
1012
1161
  :param local: Local name of the input that will be checked
1013
1162
  """
1014
- return (isinstance(local, str) and
1015
- (local in self) and
1016
- (self[local].latest_rhdict('get')))
1163
+ return (
1164
+ isinstance(local, str)
1165
+ and (local in self)
1166
+ and (self[local].latest_rhdict("get"))
1167
+ )
1017
1168
 
1018
1169
  def _grep_stuff(self, internal, action, skeleton=dict()):
1019
1170
  stack = []
@@ -1027,7 +1178,7 @@ class LocalTracker(defaultdict):
1027
1178
  :param action: Action that is considered.
1028
1179
  :param skeleton: Dictionary that will be used as a search pattern
1029
1180
  """
1030
- return self._grep_stuff('uri', action, skeleton)
1181
+ return self._grep_stuff("uri", action, skeleton)
1031
1182
 
1032
1183
  def json_dump(self, filename=_default_json_filename):
1033
1184
  """Dump the object to a JSON file.
@@ -1035,7 +1186,7 @@ class LocalTracker(defaultdict):
1035
1186
  :param filename: Path to the JSON file.
1036
1187
  """
1037
1188
  outdict = {loc: entry.dump_as_dict() for loc, entry in self.items()}
1038
- with open(filename, 'w', encoding='utf-8') as fpout:
1189
+ with open(filename, "w", encoding="utf-8") as fpout:
1039
1190
  json.dump(outdict, fpout, indent=2, sort_keys=True)
1040
1191
 
1041
1192
  def json_load(self, filename=_default_json_filename):
@@ -1043,7 +1194,7 @@ class LocalTracker(defaultdict):
1043
1194
 
1044
1195
  :param filename: Path to the JSON file.
1045
1196
  """
1046
- with open(filename, encoding='utf-8') as fpin:
1197
+ with open(filename, encoding="utf-8") as fpin:
1047
1198
  indict = json.load(fpin)
1048
1199
  # Start from scratch
1049
1200
  self.clear()
@@ -1061,7 +1212,7 @@ class LocalTracker(defaultdict):
1061
1212
  self.append(other)
1062
1213
 
1063
1214
  def __str__(self):
1064
- out = ''
1215
+ out = ""
1065
1216
  for loc, entry in self.items():
1066
1217
  entryout = str(entry)
1067
1218
  if entryout: