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
@@ -8,6 +8,7 @@ import functools
8
8
  from bronx.fancies import loggers
9
9
  import footprints
10
10
 
11
+ from vortex import config
11
12
  from .services import Service
12
13
 
13
14
  __all__ = []
@@ -20,18 +21,18 @@ class Scheduler(Service):
20
21
 
21
22
  _abstract = True
22
23
  _footprint = dict(
23
- info = 'Scheduling service class',
24
- attr = dict(
25
- muteset = dict(
26
- optional = True,
27
- default = footprints.FPSet(),
28
- type = footprints.FPSet,
24
+ info="Scheduling service class",
25
+ attr=dict(
26
+ muteset=dict(
27
+ optional=True,
28
+ default=footprints.FPSet(),
29
+ type=footprints.FPSet,
29
30
  )
30
- )
31
+ ),
31
32
  )
32
33
 
33
34
  def __init__(self, *args, **kw):
34
- logger.debug('Scheduler init %s', self.__class__)
35
+ logger.debug("Scheduler init %s", self.__class__)
35
36
  super().__init__(*args, **kw)
36
37
 
37
38
  @property
@@ -61,15 +62,15 @@ class EcmwfLikeScheduler(Scheduler):
61
62
 
62
63
  _abstract = True
63
64
  _footprint = dict(
64
- attr = dict(
65
- env_pattern = dict(
66
- info = 'Scheduler configuration variables start with...',
65
+ attr=dict(
66
+ env_pattern=dict(
67
+ info="Scheduler configuration variables start with...",
67
68
  ),
68
- non_critical_timeout = dict(
69
- info = 'Timeout in seconds for non-critical commands',
70
- type = int,
71
- default = 5,
72
- optional = True,
69
+ non_critical_timeout=dict(
70
+ info="Timeout in seconds for non-critical commands",
71
+ type=int,
72
+ default=5,
73
+ optional=True,
73
74
  ),
74
75
  )
75
76
  )
@@ -77,17 +78,23 @@ class EcmwfLikeScheduler(Scheduler):
77
78
  _KNOWN_CMD = ()
78
79
 
79
80
  def __init__(self, *args, **kw):
80
- logger.debug('Scheduler init %s', self.__class__)
81
+ logger.debug("Scheduler init %s", self.__class__)
81
82
  super(Scheduler, self).__init__(*args, **kw)
82
83
  self._inside_child_session = False
83
84
 
84
85
  def conf(self, kwenv):
85
86
  """Possibly export the provided variables and return a dictionary of positioned variables."""
86
87
  if kwenv:
87
- for schedvar in [x.upper() for x in kwenv.keys() if x.upper().startswith(self.env_pattern)]:
88
+ for schedvar in [
89
+ x.upper()
90
+ for x in kwenv.keys()
91
+ if x.upper().startswith(self.env_pattern)
92
+ ]:
88
93
  self.env[schedvar] = str(kwenv[schedvar])
89
94
  subenv = dict()
90
- for schedvar in [x for x in self.env.keys() if x.startswith(self.env_pattern)]:
95
+ for schedvar in [
96
+ x for x in self.env.keys() if x.startswith(self.env_pattern)
97
+ ]:
91
98
  subenv[schedvar] = self.env.get(schedvar)
92
99
  return subenv
93
100
 
@@ -141,27 +148,45 @@ class EcmwfLikeScheduler(Scheduler):
141
148
  rc = None
142
149
  cmd = self.cmd_rename(cmd)
143
150
  if cmd in self.muteset:
144
- logger.warning('%s mute command [%s]', self.kind, cmd)
151
+ logger.warning("%s mute command [%s]", self.kind, cmd)
145
152
  else:
146
153
  with self.child_session() as session_rc:
147
154
  if session_rc:
148
- if getattr(self, 'setup_' + cmd, self.setup_default)(*options):
155
+ if getattr(self, "setup_" + cmd, self.setup_default)(
156
+ *options
157
+ ):
149
158
  wrapp_rc = False
150
159
  try:
151
- with self.wrap_actual_child_command(kwoptions) as wrapp_rc:
160
+ with self.wrap_actual_child_command(
161
+ kwoptions
162
+ ) as wrapp_rc:
152
163
  if wrapp_rc:
153
- rc = self._actual_child(cmd, options, **kwoptions)
164
+ rc = self._actual_child(
165
+ cmd, options, **kwoptions
166
+ )
154
167
  else:
155
- logger.warning('Actual [%s %s] command wrap failed', self.kind, cmd)
168
+ logger.warning(
169
+ "Actual [%s %s] command wrap failed",
170
+ self.kind,
171
+ cmd,
172
+ )
156
173
  finally:
157
174
  if wrapp_rc:
158
- getattr(self, 'close_' + cmd, self.close_default)(*options)
175
+ getattr(
176
+ self, "close_" + cmd, self.close_default
177
+ )(*options)
159
178
  else:
160
- logger.warning('Actual [%s %s] command skipped due to setup action',
161
- self.kind, cmd)
179
+ logger.warning(
180
+ "Actual [%s %s] command skipped due to setup action",
181
+ self.kind,
182
+ cmd,
183
+ )
162
184
  else:
163
- logger.warning('Actual [%s %s] command skipped session setup failure',
164
- self.kind, cmd)
185
+ logger.warning(
186
+ "Actual [%s %s] command skipped session setup failure",
187
+ self.kind,
188
+ cmd,
189
+ )
165
190
  return rc
166
191
 
167
192
  def _actual_child(self, cmd, options, critical=True):
@@ -182,58 +207,62 @@ class SMS(EcmwfLikeScheduler):
182
207
  """
183
208
 
184
209
  _footprint = dict(
185
- info = 'SMS client service',
186
- attr = dict(
187
- kind = dict(
188
- values = ['sms'],
210
+ info="SMS client service",
211
+ attr=dict(
212
+ kind=dict(
213
+ values=["sms"],
189
214
  ),
190
- rootdir = dict(
191
- optional = True,
192
- default = None,
193
- alias = ('install',),
215
+ rootdir=dict(
216
+ optional=True,
217
+ default=None,
218
+ alias=("install",),
194
219
  ),
195
- env_pattern = dict(
196
- default = 'SMS',
197
- optional = True,
198
- )
199
- )
220
+ env_pattern=dict(
221
+ default="SMS",
222
+ optional=True,
223
+ ),
224
+ ),
200
225
  )
201
226
 
202
- _KNOWN_CMD = ('abort', 'complete', 'event', 'init', 'label', 'meter',
203
- 'msg', 'variable', 'fix')
227
+ _KNOWN_CMD = (
228
+ "abort",
229
+ "complete",
230
+ "event",
231
+ "init",
232
+ "label",
233
+ "meter",
234
+ "msg",
235
+ "variable",
236
+ "fix",
237
+ )
204
238
 
205
239
  def __init__(self, *args, **kw):
206
- logger.debug('SMS scheduler client init %s', self)
240
+ logger.debug("SMS scheduler client init %s", self)
207
241
  super().__init__(*args, **kw)
208
242
  self._actual_rootdir = self.rootdir
209
243
  if self._actual_rootdir is None:
210
244
  self._actual_rootdir = (
211
- self.env.SMS_INSTALL_ROOT or
212
- from_config(section="sms", key="rootdir")
245
+ self.env.SMS_INSTALL_ROOT
246
+ or config.from_config(section="sms", key="rootdir")
213
247
  )
214
- if self._actual_rootdir is None:
215
- logger.warning(
216
- 'SMS service could not guess install location [%s]',
217
- str(guesspath)
218
- )
219
- if self.sh.path.exists(self.cmdpath('init')):
248
+ if self.sh.path.exists(self.cmdpath("init")):
220
249
  self.env.setbinpath(self._actual_rootdir)
221
250
  else:
222
251
  logger.warning(
223
- 'No SMS client found at init time [rootdir:%s]>',
224
- self._actual_rootdir
252
+ "No SMS client found at init time [rootdir:%s]>",
253
+ self._actual_rootdir,
225
254
  )
226
255
 
227
256
  def cmd_rename(self, cmd):
228
257
  """Remap command name. Strip any sms prefix."""
229
258
  cmd = super().cmd_rename(cmd)
230
- while cmd.startswith('sms'):
259
+ while cmd.startswith("sms"):
231
260
  cmd = cmd[3:]
232
261
  return cmd
233
262
 
234
263
  def cmdpath(self, cmd):
235
264
  """Return a complete binary path to cmd."""
236
- cmd = 'sms' + self.cmd_rename(cmd)
265
+ cmd = "sms" + self.cmd_rename(cmd)
237
266
  if self._actual_rootdir:
238
267
  return self.sh.path.join(self._actual_rootdir, cmd)
239
268
  else:
@@ -255,12 +284,12 @@ class SMS(EcmwfLikeScheduler):
255
284
  """Last minute wrap before binary child command."""
256
285
  with super().wrap_actual_child_command(kwoptions) as wrapp_rc:
257
286
  upd_env = dict()
258
- if not kwoptions.get('critical', True):
259
- upd_env['SMSDENIED'] = 1
287
+ if not kwoptions.get("critical", True):
288
+ upd_env["SMSDENIED"] = 1
260
289
  if self.non_critical_timeout:
261
- upd_env['SMSTIMEOUT'] = self.non_critical_timeout
290
+ upd_env["SMSTIMEOUT"] = self.non_critical_timeout
262
291
  if upd_env:
263
- with self.env.delta_context(** upd_env):
292
+ with self.env.delta_context(**upd_env):
264
293
  yield wrapp_rc
265
294
  else:
266
295
  yield wrapp_rc
@@ -278,12 +307,12 @@ class SMSColor(SMS):
278
307
  """
279
308
 
280
309
  _footprint = dict(
281
- info = 'SMS color client service',
282
- attr = dict(
283
- kind = dict(
284
- values = ['smscolor'],
310
+ info="SMS color client service",
311
+ attr=dict(
312
+ kind=dict(
313
+ values=["smscolor"],
285
314
  ),
286
- )
315
+ ),
287
316
  )
288
317
 
289
318
  @contextlib.contextmanager
@@ -300,28 +329,39 @@ class EcFlow(EcmwfLikeScheduler):
300
329
  """
301
330
 
302
331
  _footprint = dict(
303
- info = 'SMS client service',
304
- attr = dict(
305
- kind = dict(
306
- values = ['ecflow'],
332
+ info="SMS client service",
333
+ attr=dict(
334
+ kind=dict(
335
+ values=["ecflow"],
307
336
  ),
308
- clientpath = dict(
309
- info = ("Path to the ecFlow client binary (if omitted, " +
310
- "it's read in the configuration file)"),
311
- optional = True,
312
- default = None,
337
+ clientpath=dict(
338
+ info=(
339
+ "Path to the ecFlow client binary (if omitted, "
340
+ + "it's read in the configuration file)"
341
+ ),
342
+ optional=True,
343
+ default=None,
313
344
  ),
314
- env_pattern = dict(
315
- default = 'ECF_',
316
- optional = True,
317
- )
318
- )
345
+ env_pattern=dict(
346
+ default="ECF_",
347
+ optional=True,
348
+ ),
349
+ ),
319
350
  )
320
351
 
321
- _KNOWN_CMD = ('abort', 'complete', 'event', 'init', 'label', 'meter', 'msg', 'alter')
352
+ _KNOWN_CMD = (
353
+ "abort",
354
+ "complete",
355
+ "event",
356
+ "init",
357
+ "label",
358
+ "meter",
359
+ "msg",
360
+ "alter",
361
+ )
322
362
 
323
363
  def __init__(self, *args, **kw):
324
- logger.debug('EcFlow scheduler client init %s', self)
364
+ logger.debug("EcFlow scheduler client init %s", self)
325
365
  super().__init__(*args, **kw)
326
366
  self._actual_clientpath = self.clientpath
327
367
 
@@ -329,15 +369,23 @@ class EcFlow(EcmwfLikeScheduler):
329
369
  """Return the actual binary path to the EcFlow client."""
330
370
  if self._actual_clientpath is None:
331
371
  thistarget = self.sh.default_target
332
- guesspath = self.env.ECF_CLIENT_PATH or thistarget.get('ecflow:clientpath')
333
- ecfversion = self.env.get('ECF_VERSION', 'default')
372
+ guesspath = self.env.ECF_CLIENT_PATH or thistarget.get(
373
+ "ecflow:clientpath"
374
+ )
375
+ ecfversion = self.env.get("ECF_VERSION", "default")
334
376
  guesspath = guesspath.format(version=ecfversion)
335
377
  if guesspath is None:
336
- logger.warning('ecFlow service could not guess the install location [%s]', str(guesspath))
378
+ logger.warning(
379
+ "ecFlow service could not guess the install location [%s]",
380
+ str(guesspath),
381
+ )
337
382
  else:
338
383
  self._actual_clientpath = guesspath
339
384
  if not self.sh.path.exists(self._actual_clientpath):
340
- logger.warning('No ecFlow client found at init time [path:%s]>', self._actual_clientpath)
385
+ logger.warning(
386
+ "No ecFlow client found at init time [path:%s]>",
387
+ self._actual_clientpath,
388
+ )
341
389
  return self._actual_clientpath
342
390
 
343
391
  @contextlib.contextmanager
@@ -348,24 +396,39 @@ class EcFlow(EcmwfLikeScheduler):
348
396
  tunnel = None
349
397
  # wait and retries from config
350
398
  thistarget = self.sh.default_target
351
- sshwait = float(thistarget.get('ecflow:sshproxy_wait', 6))
352
- sshretries = float(thistarget.get('ecflow:sshproxy_retries', 2))
353
- sshretrydelay = float(thistarget.get('ecflow:sshproxy_retrydelay', 1))
399
+ sshwait = float(thistarget.get("ecflow:sshproxy_wait", 6))
400
+ sshretries = float(
401
+ thistarget.get("ecflow:sshproxy_retries", 2)
402
+ )
403
+ sshretrydelay = float(
404
+ thistarget.get("ecflow:sshproxy_retrydelay", 1)
405
+ )
354
406
  # Build up an SSH tunnel to convey the EcFlow command
355
407
  ecconf = self.conf(dict())
356
- echost = ecconf.get('{:s}HOST'.format(self.env_pattern), None)
357
- ecport = ecconf.get('{:s}PORT'.format(self.env_pattern), None)
408
+ echost = ecconf.get("{:s}HOST".format(self.env_pattern), None)
409
+ ecport = ecconf.get("{:s}PORT".format(self.env_pattern), None)
358
410
  if not (echost and ecport):
359
411
  setup_rc = False
360
412
  else:
361
- sshobj = self.sh.ssh('network', virtualnode=True, mandatory_hostcheck=False,
362
- maxtries=sshretries, triesdelay=sshretrydelay)
363
- tunnel = sshobj.tunnel(echost, int(ecport), maxwait=sshwait)
413
+ sshobj = self.sh.ssh(
414
+ "network",
415
+ virtualnode=True,
416
+ mandatory_hostcheck=False,
417
+ maxtries=sshretries,
418
+ triesdelay=sshretrydelay,
419
+ )
420
+ tunnel = sshobj.tunnel(
421
+ echost, int(ecport), maxwait=sshwait
422
+ )
364
423
  if not tunnel:
365
424
  setup_rc = False
366
425
  else:
367
- newvars = {'{:s}HOST'.format(self.env_pattern): 'localhost',
368
- '{:s}PORT'.format(self.env_pattern): tunnel.entranceport}
426
+ newvars = {
427
+ "{:s}HOST".format(self.env_pattern): "localhost",
428
+ "{:s}PORT".format(
429
+ self.env_pattern
430
+ ): tunnel.entranceport,
431
+ }
369
432
  self.env.update(**newvars)
370
433
  try:
371
434
  yield setup_rc
@@ -381,27 +444,31 @@ class EcFlow(EcmwfLikeScheduler):
381
444
  """Last minute wrap before binary child command."""
382
445
  with super().wrap_actual_child_command(kwoptions) as wrapp_rc:
383
446
  upd_env = dict()
384
- if not kwoptions.get('critical', True):
385
- upd_env['{:s}DENIED'.format(self.env_pattern)] = 1
447
+ if not kwoptions.get("critical", True):
448
+ upd_env["{:s}DENIED".format(self.env_pattern)] = 1
386
449
  if self.non_critical_timeout:
387
- upd_env['{:s}TIMEOUT'.format(self.env_pattern)] = self.non_critical_timeout
450
+ upd_env["{:s}TIMEOUT".format(self.env_pattern)] = (
451
+ self.non_critical_timeout
452
+ )
388
453
  if upd_env:
389
- with self.env.delta_context(** upd_env):
454
+ with self.env.delta_context(**upd_env):
390
455
  yield wrapp_rc
391
456
  else:
392
457
  yield wrapp_rc
393
458
 
394
459
  def _actual_child(self, cmd, options, critical=True):
395
460
  """Miscellaneous ecFlow sub-command."""
396
- args = [self.path(), ]
461
+ args = [
462
+ self.path(),
463
+ ]
397
464
  if options:
398
- args.append('--{:s}={!s}'.format(cmd, options[0]))
465
+ args.append("--{:s}={!s}".format(cmd, options[0]))
399
466
  if len(options) > 1:
400
467
  args.extend(options[1:])
401
468
  else:
402
- args.append('--{:s}'.format(cmd))
469
+ args.append("--{:s}".format(cmd))
403
470
  args = [str(a) for a in args]
404
- logger.info('Issuing the ecFlow command: %s', ' '.join(args[1:]))
471
+ logger.info("Issuing the ecFlow command: %s", " ".join(args[1:]))
405
472
  return self.sh.spawn(args, output=False, fatal=critical)
406
473
 
407
474
  def abort(self, *opts):
@@ -410,4 +477,4 @@ class EcFlow(EcmwfLikeScheduler):
410
477
  if not actual_opts:
411
478
  # For backward compatibility with SMS
412
479
  actual_opts.append("No abort reason provided")
413
- return self.child('abort', *actual_opts)
480
+ return self.child("abort", *actual_opts)