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/tools/addons.py CHANGED
@@ -23,53 +23,50 @@ class Addon(footprints.FootprintBase):
23
23
  """Root class for any :class:`Addon` system subclasses."""
24
24
 
25
25
  _abstract = True
26
- _collector = ('addon',)
26
+ _collector = ("addon",)
27
27
  _footprint = dict(
28
- info = 'Default add-on',
29
- attr = dict(
30
- kind = dict(),
31
- sh = dict(
32
- type = OSExtended,
33
- alias = ('shell',),
34
- access = 'rwx-weak',
28
+ info="Default add-on",
29
+ attr=dict(
30
+ kind=dict(),
31
+ sh=dict(
32
+ type=OSExtended,
33
+ alias=("shell",),
34
+ access="rwx-weak",
35
35
  ),
36
- env = dict(
37
- type = Environment,
38
- optional = True,
39
- default = None,
40
- access = 'rwx',
41
- doc_visibility = footprints.doc.visibility.ADVANCED
36
+ env=dict(
37
+ type=Environment,
38
+ optional=True,
39
+ default=None,
40
+ access="rwx",
41
+ doc_visibility=footprints.doc.visibility.ADVANCED,
42
42
  ),
43
- cfginfo = dict(
44
- optional = True,
45
- default = '[kind]',
46
- doc_visibility = footprints.doc.visibility.ADVANCED
43
+ cfginfo=dict(
44
+ optional=True,
45
+ default="[kind]",
46
+ doc_visibility=footprints.doc.visibility.ADVANCED,
47
47
  ),
48
- cmd = dict(
49
- optional = True,
50
- default = None,
51
- access = 'rwx',
48
+ cmd=dict(
49
+ optional=True,
50
+ default=None,
51
+ access="rwx",
52
52
  ),
53
- path = dict(
54
- optional = True,
55
- default = None,
56
- access = 'rwx',
53
+ path=dict(
54
+ optional=True,
55
+ default=None,
56
+ access="rwx",
57
57
  ),
58
- cycle = dict(
59
- optional = True,
60
- default = None,
61
- access = 'rwx',
58
+ cycle=dict(
59
+ optional=True,
60
+ default=None,
61
+ access="rwx",
62
62
  ),
63
- toolkind = dict(
64
- optional = True,
65
- default = None
66
- ),
67
- )
63
+ toolkind=dict(optional=True, default=None),
64
+ ),
68
65
  )
69
66
 
70
67
  def __init__(self, *args, **kw):
71
68
  """Abstract Addon initialisation."""
72
- logger.debug('Abstract Addon init %s', self.__class__)
69
+ logger.debug("Abstract Addon init %s", self.__class__)
73
70
  super().__init__(*args, **kw)
74
71
  self.sh.extend(self)
75
72
  self._context_cache = defaultdict(dict)
@@ -81,7 +78,9 @@ class Addon(footprints.FootprintBase):
81
78
  self.env[k] = clsenv[k]
82
79
  if self.path is None:
83
80
  self.path = get_from_config_w_default(
84
- section="nwp-tools", key=self.kind, default=None,
81
+ section="nwp-tools",
82
+ key=self.kind,
83
+ default=None,
85
84
  )
86
85
 
87
86
  @classmethod
@@ -103,36 +102,44 @@ class Addon(footprints.FootprintBase):
103
102
  if ctxtag not in self._context_cache and self.toolkind is not None:
104
103
  ltrack = contexts.current().localtracker
105
104
  # NB: 'str' is important because local might be in unicode...
106
- candidates = [str(self.sh.path.realpath(local))
107
- for local, entry in ltrack.items()
108
- if (entry.latest_rhdict('get').get('resource', dict()).get('kind', '') ==
109
- self.toolkind)]
105
+ candidates = [
106
+ str(self.sh.path.realpath(local))
107
+ for local, entry in ltrack.items()
108
+ if (
109
+ entry.latest_rhdict("get")
110
+ .get("resource", dict())
111
+ .get("kind", "")
112
+ == self.toolkind
113
+ )
114
+ ]
110
115
  if candidates:
111
116
  realpath = candidates.pop()
112
- self._context_cache[ctxtag] = dict(path=self.sh.path.dirname(realpath),
113
- cmd=self.sh.path.basename(realpath))
117
+ self._context_cache[ctxtag] = dict(
118
+ path=self.sh.path.dirname(realpath),
119
+ cmd=self.sh.path.basename(realpath),
120
+ )
114
121
  return self._context_cache[ctxtag]
115
122
 
116
123
  @property
117
124
  def actual_path(self):
118
125
  """The path that should be used in the current context."""
119
126
  infos = self._query_context()
120
- ctxpath = infos.get('path', None)
127
+ ctxpath = infos.get("path", None)
121
128
  return self.path if ctxpath is None else ctxpath
122
129
 
123
130
  @property
124
131
  def actual_cmd(self):
125
132
  """The cmd that should be used in the current context."""
126
133
  infos = self._query_context()
127
- ctxcmd = infos.get('cmd', None)
134
+ ctxcmd = infos.get("cmd", None)
128
135
  return self.cmd if ctxcmd is None else ctxcmd
129
136
 
130
137
  def _spawn_commons(self, cmd, **kw):
131
138
  """Internal method setting local environment and calling standard shell spawn."""
132
139
 
133
140
  # Is there a need for an interpreter ?
134
- if 'interpreter' in kw:
135
- cmd.insert(0, kw.pop('interpreter'))
141
+ if "interpreter" in kw:
142
+ cmd.insert(0, kw.pop("interpreter"))
136
143
  else:
137
144
  # The first element of the command line needs to be executable
138
145
  if cmd[0] not in self._cmd_xperms_cache:
@@ -141,16 +148,15 @@ class Addon(footprints.FootprintBase):
141
148
 
142
149
  # Overwrite global module env values with specific ones
143
150
  with self.sh.env.clone() as localenv:
144
-
145
151
  localenv.verbose(True, self.sh)
146
152
  localenv.update(self.env)
147
153
 
148
154
  # Check if a pipe is requested
149
- inpipe = kw.pop('inpipe', False)
155
+ inpipe = kw.pop("inpipe", False)
150
156
 
151
157
  # Ask the attached shell to run the addon command
152
158
  if inpipe:
153
- kw.setdefault('stdout', True)
159
+ kw.setdefault("stdout", True)
154
160
  rc = self.sh.popen(cmd, **kw)
155
161
  else:
156
162
  rc = self.sh.spawn(cmd, **kw)
@@ -163,7 +169,7 @@ class Addon(footprints.FootprintBase):
163
169
  # Insert the actual tool command as first argument
164
170
  cmd.insert(0, self.actual_cmd)
165
171
  if self.actual_path is not None:
166
- cmd[0] = self.actual_path + '/' + cmd[0]
172
+ cmd[0] = self.actual_path + "/" + cmd[0]
167
173
 
168
174
  return self._spawn_commons(cmd, **kw)
169
175
 
@@ -172,7 +178,7 @@ class Addon(footprints.FootprintBase):
172
178
 
173
179
  # Insert the tool path before the first argument
174
180
  if self.actual_path is not None:
175
- cmd[0] = self.actual_path + '/' + cmd[0]
181
+ cmd[0] = self.actual_path + "/" + cmd[0]
176
182
 
177
183
  return self._spawn_commons(cmd, **kw)
178
184
 
@@ -182,26 +188,28 @@ class FtrawEnableAddon(Addon):
182
188
 
183
189
  _abstract = True
184
190
  _footprint = dict(
185
- info = 'Default add-on with rawftput support.',
186
- attr = dict(
187
- rawftshell = dict(
188
- info = "Path to ftserv's concatenation shell",
189
- optional = True,
190
- default = None,
191
- access = 'rwx',
192
- doc_visibility = footprints.doc.visibility.GURU,
191
+ info="Default add-on with rawftput support.",
192
+ attr=dict(
193
+ rawftshell=dict(
194
+ info="Path to ftserv's concatenation shell",
195
+ optional=True,
196
+ default=None,
197
+ access="rwx",
198
+ doc_visibility=footprints.doc.visibility.GURU,
193
199
  ),
194
- )
200
+ ),
195
201
  )
196
202
 
197
203
  def __init__(self, *args, **kw):
198
204
  """Abstract Addon initialisation."""
199
- logger.debug('Abstract Addon init %s', self.__class__)
205
+ logger.debug("Abstract Addon init %s", self.__class__)
200
206
  super().__init__(*args, **kw)
201
207
  # If needed, look in the config file for the rawftshell
202
208
  if self.rawftshell is None:
203
209
  self.rawftshell = get_from_config_w_default(
204
- section="rawftshell", key=self.kind, default=None,
210
+ section="rawftshell",
211
+ key=self.kind,
212
+ default=None,
205
213
  )
206
214
 
207
215
 
@@ -213,52 +221,59 @@ class AddonGroup(footprints.FootprintBase):
213
221
  """
214
222
 
215
223
  _abstract = True
216
- _collector = ('addon',)
224
+ _collector = ("addon",)
217
225
  _footprint = dict(
218
- info = 'Default add-on group',
219
- attr = dict(
220
- kind = dict(),
221
- sh = dict(
222
- type = OSExtended,
223
- alias = ('shell',),
226
+ info="Default add-on group",
227
+ attr=dict(
228
+ kind=dict(),
229
+ sh=dict(
230
+ type=OSExtended,
231
+ alias=("shell",),
224
232
  ),
225
- env = dict(
226
- type = Environment,
227
- optional = True,
228
- default = None,
229
- doc_visibility = footprints.doc.visibility.ADVANCED,
233
+ env=dict(
234
+ type=Environment,
235
+ optional=True,
236
+ default=None,
237
+ doc_visibility=footprints.doc.visibility.ADVANCED,
230
238
  ),
231
- cycle = dict(
232
- optional = True,
233
- default = None,
239
+ cycle=dict(
240
+ optional=True,
241
+ default=None,
234
242
  ),
235
- verboseload = dict(
236
- optional = True,
237
- default = True,
238
- type = bool,
243
+ verboseload=dict(
244
+ optional=True,
245
+ default=True,
246
+ type=bool,
239
247
  ),
240
- )
248
+ ),
241
249
  )
242
250
 
243
251
  _addonslist = None
244
252
 
245
253
  def __init__(self, *args, **kw):
246
254
  """Abstract Addon initialisation."""
247
- logger.debug('Abstract Addon init %s', self.__class__)
255
+ logger.debug("Abstract Addon init %s", self.__class__)
248
256
  super().__init__(*args, **kw)
249
257
  self._addons_load()
250
258
 
251
259
  def _addons_load(self):
252
260
  if self._addonslist is None:
253
- raise RuntimeError("the _addonslist classe variable must be overriden.")
261
+ raise RuntimeError(
262
+ "the _addonslist classe variable must be overriden."
263
+ )
254
264
  self._load_addons_from_list(self._addonslist)
255
265
 
256
266
  def _load_addons_from_list(self, addons):
257
267
  if self.verboseload:
258
268
  logger.info("Loading the %s Addons group.", self.kind)
259
269
  for addon in addons:
260
- _shadd = footprints.proxy.addon(kind=addon, sh=self.sh, env=self.env,
261
- cycle=self.cycle, verboseload=self.verboseload)
270
+ _shadd = footprints.proxy.addon(
271
+ kind=addon,
272
+ sh=self.sh,
273
+ env=self.env,
274
+ cycle=self.cycle,
275
+ verboseload=self.verboseload,
276
+ )
262
277
  if self.verboseload:
263
278
  logger.info("%s Addon is: %s", addon, repr(_shadd))
264
279
 
@@ -270,13 +285,13 @@ def require_external_addon(*addons):
270
285
 
271
286
  If not, a :class:`RuntimeError` exception will be raised.
272
287
  """
288
+
273
289
  @nicedeco
274
290
  def r_addon_decorator(method):
275
-
276
291
  def decorated(self, *kargs, **kwargs):
277
292
  # Create a cache in self... ugly but efficient !
278
- if not hasattr(self, '_require_external_addon_check_cache'):
279
- setattr(self, '_require_external_addon_check_cache', set())
293
+ if not hasattr(self, "_require_external_addon_check_cache"):
294
+ setattr(self, "_require_external_addon_check_cache", set())
280
295
  ko_addons = set()
281
296
  loaded_addons = None
282
297
  for addon in addons:
@@ -289,9 +304,13 @@ def require_external_addon(*addons):
289
304
  else:
290
305
  ko_addons.add(addon)
291
306
  if ko_addons:
292
- raise RuntimeError('The following addons are needed to use the {:s} method: {:s}'
293
- .format(method.__name__, ', '.join(ko_addons)))
307
+ raise RuntimeError(
308
+ "The following addons are needed to use the {:s} method: {:s}".format(
309
+ method.__name__, ", ".join(ko_addons)
310
+ )
311
+ )
294
312
  return method(self, *kargs, **kwargs)
295
313
 
296
314
  return decorated
315
+
297
316
  return r_addon_decorator
vortex/tools/arm.py CHANGED
@@ -7,7 +7,7 @@ from bronx.fancies import loggers
7
7
  from vortex.util.config import load_template
8
8
 
9
9
  #: No automatic export
10
- __all__ = ['ArmForgeTool']
10
+ __all__ = ["ArmForgeTool"]
11
11
 
12
12
  logger = loggers.getLogger(__name__)
13
13
 
@@ -21,18 +21,20 @@ class ArmForgeTool:
21
21
  """
22
22
  self._t = ticket
23
23
  self._sh = self._t.sh
24
- self._config = self._sh.default_target.items('armtools')
25
- self._ddtpath = self._sh.env.get('VORTEX_ARM_DDT_PATH', None)
26
- self._mappath = self._sh.env.get('VORTEX_ARM_MAP_PATH', None)
27
- self._forgedir = self._sh.env.get('VORTEX_ARM_FORGE_DIR',
28
- self.config.get('forgedir', None))
29
- self._forgeversion = self._sh.env.get('VORTEX_ARM_FORGE_VERSION',
30
- self.config.get('forgeversion', 999999))
24
+ self._config = self._sh.default_target.items("armtools")
25
+ self._ddtpath = self._sh.env.get("VORTEX_ARM_DDT_PATH", None)
26
+ self._mappath = self._sh.env.get("VORTEX_ARM_MAP_PATH", None)
27
+ self._forgedir = self._sh.env.get(
28
+ "VORTEX_ARM_FORGE_DIR", self.config.get("forgedir", None)
29
+ )
30
+ self._forgeversion = self._sh.env.get(
31
+ "VORTEX_ARM_FORGE_VERSION", self.config.get("forgeversion", 999999)
32
+ )
31
33
  self._forgeversion = int(self._forgeversion)
32
34
  if self._forgedir and self._ddtpath is None:
33
- self._ddtpath = self._sh.path.join(self._forgedir, 'bin', 'ddt')
35
+ self._ddtpath = self._sh.path.join(self._forgedir, "bin", "ddt")
34
36
  if self._forgedir and self._mappath is None:
35
- self._mappath = self._sh.path.join(self._forgedir, 'bin', 'map')
37
+ self._mappath = self._sh.path.join(self._forgedir, "bin", "map")
36
38
 
37
39
  @property
38
40
  def config(self):
@@ -43,34 +45,52 @@ class ArmForgeTool:
43
45
  def ddtpath(self):
44
46
  """The path to the DDT debuger executable."""
45
47
  if self._ddtpath is None:
46
- raise RuntimeError('DDT requested but the DDT path is not configured.')
48
+ raise RuntimeError(
49
+ "DDT requested but the DDT path is not configured."
50
+ )
47
51
  return self._ddtpath
48
52
 
49
53
  @property
50
54
  def mappath(self):
51
55
  """The path to the MAP profiler executable."""
52
56
  if self._mappath is None:
53
- raise RuntimeError('MAP requested but the MAP path is not configured.')
57
+ raise RuntimeError(
58
+ "MAP requested but the MAP path is not configured."
59
+ )
54
60
  return self._mappath
55
61
 
56
62
  def _dump_forge_session(self, sources=(), workdir=None):
57
63
  """Create the ARM Forge's session file to list source directories."""
58
- targetfile = 'armforge-vortex-session-file.ddt'
64
+ targetfile = "armforge-vortex-session-file.ddt"
59
65
  if workdir:
60
66
  targetfile = self._sh.path.join(workdir, targetfile)
61
- tpl = load_template(self._t, '@armforge-session-conf.tpl',
62
- encoding='utf-8', version=self._forgeversion)
63
- sconf = tpl.substitute(sourcedirs='\n'.join([' <directory>{:s}</directory>'.format(d)
64
- for d in sources]))
65
- with open(targetfile, 'w') as fhs:
67
+ tpl = load_template(
68
+ self._t,
69
+ "@armforge-session-conf.tpl",
70
+ encoding="utf-8",
71
+ version=self._forgeversion,
72
+ )
73
+ sconf = tpl.substitute(
74
+ sourcedirs="\n".join(
75
+ [
76
+ " <directory>{:s}</directory>".format(d)
77
+ for d in sources
78
+ ]
79
+ )
80
+ )
81
+ with open(targetfile, "w") as fhs:
66
82
  fhs.write(sconf)
67
83
  return targetfile
68
84
 
69
85
  def ddt_prefix_cmd(self, sources=(), workdir=None):
70
86
  """Generate the prefix command required to start DDT."""
71
87
  if sources:
72
- return [self.ddtpath,
73
- '--session={:s}'.format(self._dump_forge_session(sources, workdir=workdir)),
74
- '--connect']
88
+ return [
89
+ self.ddtpath,
90
+ "--session={:s}".format(
91
+ self._dump_forge_session(sources, workdir=workdir)
92
+ ),
93
+ "--connect",
94
+ ]
75
95
  else:
76
- return [self.ddtpath, '--connect']
96
+ return [self.ddtpath, "--connect"]