vortex-nwp 2.0.0b1__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.
- vortex/__init__.py +135 -0
- vortex/algo/__init__.py +12 -0
- vortex/algo/components.py +2136 -0
- vortex/algo/mpitools.py +1648 -0
- vortex/algo/mpitools_templates/envelope_wrapper_default.tpl +27 -0
- vortex/algo/mpitools_templates/envelope_wrapper_mpiauto.tpl +29 -0
- vortex/algo/mpitools_templates/wrapstd_wrapper_default.tpl +18 -0
- vortex/algo/serversynctools.py +170 -0
- vortex/config.py +115 -0
- vortex/data/__init__.py +13 -0
- vortex/data/abstractstores.py +1572 -0
- vortex/data/containers.py +780 -0
- vortex/data/contents.py +596 -0
- vortex/data/executables.py +284 -0
- vortex/data/flow.py +113 -0
- vortex/data/geometries.ini +2689 -0
- vortex/data/geometries.py +703 -0
- vortex/data/handlers.py +1021 -0
- vortex/data/outflow.py +67 -0
- vortex/data/providers.py +465 -0
- vortex/data/resources.py +201 -0
- vortex/data/stores.py +1271 -0
- vortex/gloves.py +282 -0
- vortex/layout/__init__.py +27 -0
- vortex/layout/appconf.py +109 -0
- vortex/layout/contexts.py +511 -0
- vortex/layout/dataflow.py +1069 -0
- vortex/layout/jobs.py +1276 -0
- vortex/layout/monitor.py +833 -0
- vortex/layout/nodes.py +1424 -0
- vortex/layout/subjobs.py +464 -0
- vortex/nwp/__init__.py +11 -0
- vortex/nwp/algo/__init__.py +12 -0
- vortex/nwp/algo/assim.py +483 -0
- vortex/nwp/algo/clim.py +920 -0
- vortex/nwp/algo/coupling.py +609 -0
- vortex/nwp/algo/eda.py +632 -0
- vortex/nwp/algo/eps.py +613 -0
- vortex/nwp/algo/forecasts.py +745 -0
- vortex/nwp/algo/fpserver.py +927 -0
- vortex/nwp/algo/ifsnaming.py +403 -0
- vortex/nwp/algo/ifsroot.py +311 -0
- vortex/nwp/algo/monitoring.py +202 -0
- vortex/nwp/algo/mpitools.py +554 -0
- vortex/nwp/algo/odbtools.py +974 -0
- vortex/nwp/algo/oopsroot.py +735 -0
- vortex/nwp/algo/oopstests.py +186 -0
- vortex/nwp/algo/request.py +579 -0
- vortex/nwp/algo/stdpost.py +1285 -0
- vortex/nwp/data/__init__.py +12 -0
- vortex/nwp/data/assim.py +392 -0
- vortex/nwp/data/boundaries.py +261 -0
- vortex/nwp/data/climfiles.py +539 -0
- vortex/nwp/data/configfiles.py +149 -0
- vortex/nwp/data/consts.py +929 -0
- vortex/nwp/data/ctpini.py +133 -0
- vortex/nwp/data/diagnostics.py +181 -0
- vortex/nwp/data/eda.py +148 -0
- vortex/nwp/data/eps.py +383 -0
- vortex/nwp/data/executables.py +1039 -0
- vortex/nwp/data/fields.py +96 -0
- vortex/nwp/data/gridfiles.py +308 -0
- vortex/nwp/data/logs.py +551 -0
- vortex/nwp/data/modelstates.py +334 -0
- vortex/nwp/data/monitoring.py +220 -0
- vortex/nwp/data/namelists.py +644 -0
- vortex/nwp/data/obs.py +748 -0
- vortex/nwp/data/oopsexec.py +72 -0
- vortex/nwp/data/providers.py +182 -0
- vortex/nwp/data/query.py +217 -0
- vortex/nwp/data/stores.py +147 -0
- vortex/nwp/data/surfex.py +338 -0
- vortex/nwp/syntax/__init__.py +9 -0
- vortex/nwp/syntax/stdattrs.py +375 -0
- vortex/nwp/tools/__init__.py +10 -0
- vortex/nwp/tools/addons.py +35 -0
- vortex/nwp/tools/agt.py +55 -0
- vortex/nwp/tools/bdap.py +48 -0
- vortex/nwp/tools/bdcp.py +38 -0
- vortex/nwp/tools/bdm.py +21 -0
- vortex/nwp/tools/bdmp.py +49 -0
- vortex/nwp/tools/conftools.py +1311 -0
- vortex/nwp/tools/drhook.py +62 -0
- vortex/nwp/tools/grib.py +268 -0
- vortex/nwp/tools/gribdiff.py +99 -0
- vortex/nwp/tools/ifstools.py +163 -0
- vortex/nwp/tools/igastuff.py +249 -0
- vortex/nwp/tools/mars.py +56 -0
- vortex/nwp/tools/odb.py +548 -0
- vortex/nwp/tools/partitioning.py +234 -0
- vortex/nwp/tools/satrad.py +56 -0
- vortex/nwp/util/__init__.py +6 -0
- vortex/nwp/util/async.py +184 -0
- vortex/nwp/util/beacon.py +40 -0
- vortex/nwp/util/diffpygram.py +359 -0
- vortex/nwp/util/ens.py +198 -0
- vortex/nwp/util/hooks.py +128 -0
- vortex/nwp/util/taskdeco.py +81 -0
- vortex/nwp/util/usepygram.py +591 -0
- vortex/nwp/util/usetnt.py +87 -0
- vortex/proxy.py +6 -0
- vortex/sessions.py +341 -0
- vortex/syntax/__init__.py +9 -0
- vortex/syntax/stdattrs.py +628 -0
- vortex/syntax/stddeco.py +176 -0
- vortex/toolbox.py +982 -0
- vortex/tools/__init__.py +11 -0
- vortex/tools/actions.py +457 -0
- vortex/tools/addons.py +297 -0
- vortex/tools/arm.py +76 -0
- vortex/tools/compression.py +322 -0
- vortex/tools/date.py +20 -0
- vortex/tools/ddhpack.py +10 -0
- vortex/tools/delayedactions.py +672 -0
- vortex/tools/env.py +513 -0
- vortex/tools/folder.py +663 -0
- vortex/tools/grib.py +559 -0
- vortex/tools/lfi.py +746 -0
- vortex/tools/listings.py +354 -0
- vortex/tools/names.py +575 -0
- vortex/tools/net.py +1790 -0
- vortex/tools/odb.py +10 -0
- vortex/tools/parallelism.py +336 -0
- vortex/tools/prestaging.py +186 -0
- vortex/tools/rawfiles.py +10 -0
- vortex/tools/schedulers.py +413 -0
- vortex/tools/services.py +871 -0
- vortex/tools/storage.py +1061 -0
- vortex/tools/surfex.py +61 -0
- vortex/tools/systems.py +3396 -0
- vortex/tools/targets.py +384 -0
- vortex/util/__init__.py +9 -0
- vortex/util/config.py +1071 -0
- vortex/util/empty.py +24 -0
- vortex/util/helpers.py +184 -0
- vortex/util/introspection.py +63 -0
- vortex/util/iosponge.py +76 -0
- vortex/util/roles.py +51 -0
- vortex/util/storefunctions.py +103 -0
- vortex/util/structs.py +26 -0
- vortex/util/worker.py +150 -0
- vortex_nwp-2.0.0b1.dist-info/LICENSE +517 -0
- vortex_nwp-2.0.0b1.dist-info/METADATA +50 -0
- vortex_nwp-2.0.0b1.dist-info/RECORD +146 -0
- vortex_nwp-2.0.0b1.dist-info/WHEEL +5 -0
- vortex_nwp-2.0.0b1.dist-info/top_level.txt +1 -0
vortex/nwp/data/logs.py
ADDED
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO: Module documentation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import collections.abc
|
|
6
|
+
|
|
7
|
+
from bronx.stdtypes.date import Date, Time
|
|
8
|
+
from vortex import sessions
|
|
9
|
+
from vortex.data.contents import DataContent, JsonDictContent, FormatAdapter
|
|
10
|
+
from vortex.data.flow import FlowResource
|
|
11
|
+
from vortex.data.resources import Resource
|
|
12
|
+
from vortex.syntax.stdattrs import FmtInt, date_deco, cutoff_deco
|
|
13
|
+
from vortex.syntax.stddeco import namebuilding_delete, namebuilding_insert
|
|
14
|
+
from vortex.util.roles import setrole
|
|
15
|
+
|
|
16
|
+
#: No automatic export
|
|
17
|
+
__all__ = []
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FlowLogsStack(Resource):
|
|
21
|
+
"""Stack of miscellaneous log files"""
|
|
22
|
+
|
|
23
|
+
_footprint = [
|
|
24
|
+
date_deco,
|
|
25
|
+
cutoff_deco,
|
|
26
|
+
dict(
|
|
27
|
+
info = 'Stack of miscellaneous log files.',
|
|
28
|
+
attr = dict(
|
|
29
|
+
kind = dict(
|
|
30
|
+
values = ['flow_logs']
|
|
31
|
+
),
|
|
32
|
+
nativefmt = dict(
|
|
33
|
+
values = ['filespack', ],
|
|
34
|
+
default = 'filespack',
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def realkind(self):
|
|
42
|
+
return 'flow_logs'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def use_flow_logs_stack(cls):
|
|
46
|
+
"""Setup the decorated class to work with the FlowLogsStack resource."""
|
|
47
|
+
fpattrs = set(cls.footprint_retrieve().attr.keys())
|
|
48
|
+
fpcheck = all([k in fpattrs for k in ('date', 'cutoff')])
|
|
49
|
+
if not fpcheck:
|
|
50
|
+
raise ImportError('The "{!s}" class is not compatible with the FlowLogsStack class.'
|
|
51
|
+
.format(cls))
|
|
52
|
+
|
|
53
|
+
def stackedstorage_resource(self):
|
|
54
|
+
"""Use the FlowLogsStack resource for stacked storage."""
|
|
55
|
+
return FlowLogsStack(kind='flow_logs', date=self.date, cutoff=self.cutoff), False
|
|
56
|
+
|
|
57
|
+
cls.stackedstorage_resource = stackedstorage_resource
|
|
58
|
+
return cls
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@use_flow_logs_stack
|
|
62
|
+
@namebuilding_insert('src', lambda s: [s.binary, '-'.join(s.task.split('/')[s.task_start:s.task_stop])])
|
|
63
|
+
@namebuilding_insert('compute', lambda s: s.part)
|
|
64
|
+
@namebuilding_delete('fmt')
|
|
65
|
+
class Listing(FlowResource):
|
|
66
|
+
"""Miscellaneous application output from a task processing."""
|
|
67
|
+
_footprint = [
|
|
68
|
+
dict(
|
|
69
|
+
info = 'Listing',
|
|
70
|
+
attr = dict(
|
|
71
|
+
task = dict(
|
|
72
|
+
optional = True,
|
|
73
|
+
default = 'anonymous'
|
|
74
|
+
),
|
|
75
|
+
task_start = dict(
|
|
76
|
+
optional = True,
|
|
77
|
+
type = int,
|
|
78
|
+
default = -1,
|
|
79
|
+
),
|
|
80
|
+
task_stop = dict(
|
|
81
|
+
optional = True,
|
|
82
|
+
type = int,
|
|
83
|
+
default = None,
|
|
84
|
+
),
|
|
85
|
+
kind = dict(
|
|
86
|
+
values = ['listing']
|
|
87
|
+
),
|
|
88
|
+
part = dict(
|
|
89
|
+
optional = True,
|
|
90
|
+
default = 'all',
|
|
91
|
+
),
|
|
92
|
+
binary = dict(
|
|
93
|
+
optional = True,
|
|
94
|
+
default = '[model]',
|
|
95
|
+
),
|
|
96
|
+
clscontents = dict(
|
|
97
|
+
default = FormatAdapter,
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def realkind(self):
|
|
105
|
+
return 'listing'
|
|
106
|
+
|
|
107
|
+
def olive_basename(self):
|
|
108
|
+
"""Fake basename for getting olive listings"""
|
|
109
|
+
if hasattr(self, '_listingpath'):
|
|
110
|
+
return self._listingpath
|
|
111
|
+
else:
|
|
112
|
+
return "NOT_IMPLEMENTED"
|
|
113
|
+
|
|
114
|
+
def archive_basename(self):
|
|
115
|
+
return "listing." + self.part
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ParallelListing(Listing):
|
|
119
|
+
"""Multi output for parallel MPI and/or OpenMP processing."""
|
|
120
|
+
_footprint = [
|
|
121
|
+
dict(
|
|
122
|
+
attr = dict(
|
|
123
|
+
kind = dict(
|
|
124
|
+
values = ['listing', 'plisting', 'mlisting'],
|
|
125
|
+
remap = dict(
|
|
126
|
+
listing = 'plisting',
|
|
127
|
+
mlisting = 'plisting',
|
|
128
|
+
)
|
|
129
|
+
),
|
|
130
|
+
mpi = dict(
|
|
131
|
+
optional = True,
|
|
132
|
+
default = None,
|
|
133
|
+
type = FmtInt,
|
|
134
|
+
args = dict(fmt = '03'),
|
|
135
|
+
),
|
|
136
|
+
openmp = dict(
|
|
137
|
+
optional = True,
|
|
138
|
+
default = None,
|
|
139
|
+
type = FmtInt,
|
|
140
|
+
args = dict(fmt = '02'),
|
|
141
|
+
),
|
|
142
|
+
seta = dict(
|
|
143
|
+
optional = True,
|
|
144
|
+
default = None,
|
|
145
|
+
type = FmtInt,
|
|
146
|
+
args = dict(fmt = '03'),
|
|
147
|
+
),
|
|
148
|
+
setb = dict(
|
|
149
|
+
optional = True,
|
|
150
|
+
default = None,
|
|
151
|
+
type = FmtInt,
|
|
152
|
+
args = dict(fmt = '02'),
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
def namebuilding_info(self):
|
|
159
|
+
"""From base information of ``listing`` add mpi and openmp values."""
|
|
160
|
+
info = super().namebuilding_info()
|
|
161
|
+
if self.mpi and self.openmp:
|
|
162
|
+
info['compute'] = [{'mpi': self.mpi}, {'openmp': self.openmp}]
|
|
163
|
+
if self.seta and self.setb:
|
|
164
|
+
info['compute'] = [{'seta': self.seta}, {'setb': self.setb}]
|
|
165
|
+
return info
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@namebuilding_insert('src', lambda s: [s.binary, s.task.split('/').pop()])
|
|
169
|
+
@namebuilding_insert('compute', lambda s: s.part)
|
|
170
|
+
@namebuilding_delete('fmt')
|
|
171
|
+
class StaticListing(Resource):
|
|
172
|
+
"""Miscelanous application output from a task processing, out-of-flow."""
|
|
173
|
+
_footprint = [
|
|
174
|
+
dict(
|
|
175
|
+
info = 'Listing',
|
|
176
|
+
attr = dict(
|
|
177
|
+
task = dict(
|
|
178
|
+
optional = True,
|
|
179
|
+
default = 'anonymous'
|
|
180
|
+
),
|
|
181
|
+
kind = dict(
|
|
182
|
+
values = ['staticlisting']
|
|
183
|
+
),
|
|
184
|
+
part = dict(
|
|
185
|
+
optional = True,
|
|
186
|
+
default = 'all',
|
|
187
|
+
),
|
|
188
|
+
binary = dict(
|
|
189
|
+
optional = True,
|
|
190
|
+
default = '[model]',
|
|
191
|
+
),
|
|
192
|
+
clscontents = dict(
|
|
193
|
+
default = FormatAdapter,
|
|
194
|
+
),
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def realkind(self):
|
|
201
|
+
return 'staticlisting'
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@namebuilding_insert('compute', lambda s: None if s.mpi is None else [{'mpi': s.mpi}, ],
|
|
205
|
+
none_discard=True)
|
|
206
|
+
class DrHookListing(Listing):
|
|
207
|
+
"""Output produced by DrHook"""
|
|
208
|
+
_footprint = [
|
|
209
|
+
dict(
|
|
210
|
+
attr = dict(
|
|
211
|
+
kind = dict(
|
|
212
|
+
values = ['drhook', ],
|
|
213
|
+
),
|
|
214
|
+
mpi = dict(
|
|
215
|
+
optional = True,
|
|
216
|
+
type = FmtInt,
|
|
217
|
+
args = dict(fmt = '03'),
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def realkind(self):
|
|
225
|
+
return 'drhookprof'
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@use_flow_logs_stack
|
|
229
|
+
class Beacon(FlowResource):
|
|
230
|
+
"""Output indicating the end of a model run."""
|
|
231
|
+
_footprint = [
|
|
232
|
+
dict(
|
|
233
|
+
info = 'Beacon',
|
|
234
|
+
attr = dict(
|
|
235
|
+
kind = dict(
|
|
236
|
+
values = ['beacon']
|
|
237
|
+
),
|
|
238
|
+
clscontents = dict(
|
|
239
|
+
default = JsonDictContent,
|
|
240
|
+
),
|
|
241
|
+
nativefmt = dict(
|
|
242
|
+
default = 'json',
|
|
243
|
+
),
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def realkind(self):
|
|
250
|
+
return 'beacon'
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@use_flow_logs_stack
|
|
254
|
+
@namebuilding_insert('src', lambda s: s.task.split('/').pop())
|
|
255
|
+
@namebuilding_insert('compute', lambda s: s.scope)
|
|
256
|
+
class TaskInfo(FlowResource):
|
|
257
|
+
"""Task informations."""
|
|
258
|
+
_footprint = [
|
|
259
|
+
dict(
|
|
260
|
+
info = 'Task informations',
|
|
261
|
+
attr = dict(
|
|
262
|
+
task = dict(
|
|
263
|
+
optional = True,
|
|
264
|
+
default = 'anonymous'
|
|
265
|
+
),
|
|
266
|
+
kind = dict(
|
|
267
|
+
values = ['taskinfo']
|
|
268
|
+
),
|
|
269
|
+
scope = dict(
|
|
270
|
+
optional = True,
|
|
271
|
+
default = 'void',
|
|
272
|
+
),
|
|
273
|
+
clscontents = dict(
|
|
274
|
+
default = JsonDictContent,
|
|
275
|
+
),
|
|
276
|
+
nativefmt = dict(
|
|
277
|
+
default = 'json',
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
)
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def realkind(self):
|
|
285
|
+
return 'taskinfo'
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@namebuilding_insert('src', lambda s: s.task.split('/').pop())
|
|
289
|
+
@namebuilding_insert('compute', lambda s: s.scope)
|
|
290
|
+
@namebuilding_delete('fmt')
|
|
291
|
+
class StaticTaskInfo(Resource):
|
|
292
|
+
"""Task informations."""
|
|
293
|
+
_footprint = [
|
|
294
|
+
dict(
|
|
295
|
+
info = 'Task informations',
|
|
296
|
+
attr = dict(
|
|
297
|
+
task = dict(
|
|
298
|
+
optional = True,
|
|
299
|
+
default = 'anonymous'
|
|
300
|
+
),
|
|
301
|
+
kind = dict(
|
|
302
|
+
values = ['statictaskinfo']
|
|
303
|
+
),
|
|
304
|
+
scope = dict(
|
|
305
|
+
optional = True,
|
|
306
|
+
default = 'void',
|
|
307
|
+
),
|
|
308
|
+
clscontents = dict(
|
|
309
|
+
default = JsonDictContent,
|
|
310
|
+
),
|
|
311
|
+
nativefmt = dict(
|
|
312
|
+
default = 'json',
|
|
313
|
+
),
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def realkind(self):
|
|
320
|
+
return 'statictaskinfo'
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class SectionsSlice(collections.abc.Sequence):
|
|
324
|
+
"""Hold a list of dictionaries representing Sections."""
|
|
325
|
+
|
|
326
|
+
_INDEX_PREFIX = 'sslice'
|
|
327
|
+
_INDEX_ATTR = 'sliceindex'
|
|
328
|
+
|
|
329
|
+
def __init__(self, sequence):
|
|
330
|
+
self._data = sequence
|
|
331
|
+
|
|
332
|
+
def __getitem__(self, i):
|
|
333
|
+
if isinstance(i, str) and i.startswith(self._INDEX_PREFIX):
|
|
334
|
+
i = int(i[len(self._INDEX_PREFIX):])
|
|
335
|
+
return self._data[i]
|
|
336
|
+
|
|
337
|
+
def __eq__(self, other):
|
|
338
|
+
return self.to_list() == other.to_list()
|
|
339
|
+
|
|
340
|
+
def __len__(self):
|
|
341
|
+
return len(self._data)
|
|
342
|
+
|
|
343
|
+
def __iter__(self):
|
|
344
|
+
return iter(self._data)
|
|
345
|
+
|
|
346
|
+
def to_list(self):
|
|
347
|
+
"""Returns a list object with the exact same content."""
|
|
348
|
+
return list(self._data)
|
|
349
|
+
|
|
350
|
+
@staticmethod
|
|
351
|
+
def _sloppy_lookup(item, k):
|
|
352
|
+
"""Look for a key *k* in the *item* dictionary and returns it.
|
|
353
|
+
|
|
354
|
+
:note: A special treatment is made for the 'role' key (the role factory is used
|
|
355
|
+
and the 'alternate' attribute may also be looked for).
|
|
356
|
+
|
|
357
|
+
:note: A special case is made for the attribute 'kind' of the section which can be
|
|
358
|
+
accessed via the 'section_kind' attribute (the attribute 'kind' is used for the resource attribute).
|
|
359
|
+
|
|
360
|
+
:note: if *k* is not found at the top level of the dictionary, the
|
|
361
|
+
'resource', 'provider' and 'container' parts of the 'rh'sub-dictionary
|
|
362
|
+
are also looked for.
|
|
363
|
+
"""
|
|
364
|
+
if k == 'role':
|
|
365
|
+
return item[k] or item['alternate']
|
|
366
|
+
elif k == 'kind' and k in item.get('rh', dict()).get('resource', dict()):
|
|
367
|
+
return item['rh']['resource'][k]
|
|
368
|
+
elif k == 'section_kind' and 'kind' in item:
|
|
369
|
+
return item['kind']
|
|
370
|
+
elif k in item:
|
|
371
|
+
return item[k]
|
|
372
|
+
elif k in item.get('rh', dict()).get('resource', dict()):
|
|
373
|
+
return item['rh']['resource'][k]
|
|
374
|
+
elif k in item.get('rh', dict()).get('provider', dict()):
|
|
375
|
+
return item['rh']['provider'][k]
|
|
376
|
+
elif k in item.get('rh', dict()).get('container', dict()):
|
|
377
|
+
return item['rh']['container'][k]
|
|
378
|
+
else:
|
|
379
|
+
raise KeyError("'{:s}' wasn't found in the designated dictionary"
|
|
380
|
+
.format(k))
|
|
381
|
+
|
|
382
|
+
@staticmethod
|
|
383
|
+
def _sloppy_compare(json_v, v):
|
|
384
|
+
"""Try a very very permissive check."""
|
|
385
|
+
if callable(v):
|
|
386
|
+
try:
|
|
387
|
+
return v(json_v)
|
|
388
|
+
except (ValueError, TypeError):
|
|
389
|
+
return False
|
|
390
|
+
else:
|
|
391
|
+
try:
|
|
392
|
+
return type(v)(json_v) == v
|
|
393
|
+
except (ValueError, TypeError):
|
|
394
|
+
try:
|
|
395
|
+
return json_v == v
|
|
396
|
+
except (ValueError, TypeError):
|
|
397
|
+
return False
|
|
398
|
+
|
|
399
|
+
def _sloppy_ckeck(self, item, k, v, extras):
|
|
400
|
+
"""Perform a _sloppy_lookup and check the result against *v*."""
|
|
401
|
+
if k in ('role', 'alternate'):
|
|
402
|
+
v = setrole(v)
|
|
403
|
+
try:
|
|
404
|
+
if k == 'baseterm':
|
|
405
|
+
found = self._sloppy_lookup(item, 'term')
|
|
406
|
+
foundbis = self._sloppy_lookup(item, 'date')
|
|
407
|
+
else:
|
|
408
|
+
found = self._sloppy_lookup(item, k)
|
|
409
|
+
except KeyError:
|
|
410
|
+
return False
|
|
411
|
+
if not isinstance(v, (list, tuple, set)):
|
|
412
|
+
v = [v, ]
|
|
413
|
+
if k == 'baseterm' and extras.get('basedate', None):
|
|
414
|
+
delta = (Date(extras['basedate']) - Date(foundbis))
|
|
415
|
+
found = Time(found) - delta
|
|
416
|
+
return any([self._sloppy_compare(found, a_v) for a_v in v])
|
|
417
|
+
|
|
418
|
+
def filter(self, **kwargs):
|
|
419
|
+
"""Create a new :class:`SectionsSlice` object that will be filtered using *kwargs*.
|
|
420
|
+
|
|
421
|
+
:example: To retrieve sections with ``role=='Guess'`` and ``rh.provider.member==1``::
|
|
422
|
+
|
|
423
|
+
>>> self.filter(role='Guess', member=1)
|
|
424
|
+
"""
|
|
425
|
+
extras = dict()
|
|
426
|
+
extras['basedate'] = kwargs.pop('basedate', None)
|
|
427
|
+
newslice = [s for s in self
|
|
428
|
+
if all([self._sloppy_ckeck(s, k, v, extras)
|
|
429
|
+
for k, v in kwargs.items()])]
|
|
430
|
+
return self.__class__(newslice)
|
|
431
|
+
|
|
432
|
+
def uniquefilter(self, **kwargs):
|
|
433
|
+
"""Like :meth:`filter` but checks that only one element matches."""
|
|
434
|
+
newslice = self.filter(**kwargs)
|
|
435
|
+
if len(newslice) == 0:
|
|
436
|
+
raise ValueError("No section was found")
|
|
437
|
+
elif len(newslice) > 1:
|
|
438
|
+
raise ValueError("Multiple sections were found")
|
|
439
|
+
else:
|
|
440
|
+
return newslice
|
|
441
|
+
|
|
442
|
+
@property
|
|
443
|
+
def indexes(self):
|
|
444
|
+
"""Returns an index list of all the element contained if the present object."""
|
|
445
|
+
return [self._INDEX_PREFIX + '{:d}'.format(i) for i in range(len(self))]
|
|
446
|
+
|
|
447
|
+
def __deepcopy__(self, memo):
|
|
448
|
+
newslice = self.__class__(self._data)
|
|
449
|
+
memo[id(self)] = newslice
|
|
450
|
+
return newslice
|
|
451
|
+
|
|
452
|
+
def __getattr__(self, attr):
|
|
453
|
+
"""Provides an easy access to content's data with footprint's mechanisms.*
|
|
454
|
+
|
|
455
|
+
If the present :class:`SectionsSlice` only contains one element, a
|
|
456
|
+
:meth:`_sloppy_lookup` is performed on this unique element and returned.
|
|
457
|
+
For exemple ``self.vapp`` will be equivalent to
|
|
458
|
+
``self[0]['rh']['provider']['vapp']``.
|
|
459
|
+
|
|
460
|
+
If the present :class:`SectionsSlice` contains several elements, it's more
|
|
461
|
+
complex : a callback function is returned. Such a callback can be used
|
|
462
|
+
in conjunction with footprint's replacement mechanism. Provided that a
|
|
463
|
+
``{idx_attr:s}`` attribute exists in the footprint description and
|
|
464
|
+
can be used as an index in the present object (such a list of indexes can
|
|
465
|
+
be generated using the :meth:`indexes` property), the corresponding element
|
|
466
|
+
will be searched using :meth:`_sloppy_lookup`.
|
|
467
|
+
""".format(idx_attr=self._INDEX_ATTR)
|
|
468
|
+
if attr.startswith('__'):
|
|
469
|
+
raise AttributeError(attr)
|
|
470
|
+
if len(self) == 1:
|
|
471
|
+
try:
|
|
472
|
+
return self._sloppy_lookup(self[0], attr)
|
|
473
|
+
except KeyError:
|
|
474
|
+
raise AttributeError("'{:s}' wasn't found in the unique dictionary"
|
|
475
|
+
.format(attr))
|
|
476
|
+
elif len(self) == 0:
|
|
477
|
+
raise AttributeError("The current SectionsSlice is empty. No attribute lookup allowed !")
|
|
478
|
+
else:
|
|
479
|
+
def _attr_lookup(g, x):
|
|
480
|
+
if len(self) > 1 and (self._INDEX_ATTR in g or self._INDEX_ATTR in x):
|
|
481
|
+
idx = g.get(self._INDEX_ATTR, x.get(self._INDEX_ATTR))
|
|
482
|
+
try:
|
|
483
|
+
return self._sloppy_lookup(self[idx], attr)
|
|
484
|
+
except KeyError:
|
|
485
|
+
raise AttributeError("'{:s}' wasn't found in the {!s}-th dictionary"
|
|
486
|
+
.format(attr, idx))
|
|
487
|
+
else:
|
|
488
|
+
raise AttributeError("A '{:s}' attribute must be there !".format(self._INDEX_ATTR))
|
|
489
|
+
|
|
490
|
+
return _attr_lookup
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
class SectionsJsonListContent(DataContent):
|
|
494
|
+
"""Load/Dump a JSON file that contains a list of Sections.
|
|
495
|
+
|
|
496
|
+
The conents of the JSON file is then stored in a query-able
|
|
497
|
+
:class:`SectionsSlice` object.
|
|
498
|
+
"""
|
|
499
|
+
|
|
500
|
+
def slurp(self, container):
|
|
501
|
+
"""Get data from the ``container``."""
|
|
502
|
+
t = sessions.current()
|
|
503
|
+
with container.preferred_decoding(byte=False):
|
|
504
|
+
container.rewind()
|
|
505
|
+
self._data = SectionsSlice(t.sh.json_load(container.iotarget()))
|
|
506
|
+
self._size = len(self._data)
|
|
507
|
+
|
|
508
|
+
def rewrite(self, container):
|
|
509
|
+
"""Write the data in the specified container."""
|
|
510
|
+
t = sessions.current()
|
|
511
|
+
container.close()
|
|
512
|
+
with container.iod_context():
|
|
513
|
+
with container.preferred_decoding(byte=False):
|
|
514
|
+
with container.preferred_write():
|
|
515
|
+
iod = container.iodesc()
|
|
516
|
+
t.sh.json_dump(self.data.to_list(), iod, indent=4)
|
|
517
|
+
container.updfill(True)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
@use_flow_logs_stack
|
|
521
|
+
@namebuilding_insert('src', lambda s: s.task.split('/').pop())
|
|
522
|
+
class SectionsList(FlowResource):
|
|
523
|
+
"""Class to handle a resource that contains a list of Sections in JSON format.
|
|
524
|
+
|
|
525
|
+
Such a resource can be generated using the :class:`FunctionStore` with the
|
|
526
|
+
:func:`vortex.util.storefunctions.dumpinputs` function.
|
|
527
|
+
"""
|
|
528
|
+
|
|
529
|
+
_footprint = dict(
|
|
530
|
+
info = 'A Sections List',
|
|
531
|
+
attr = dict(
|
|
532
|
+
kind=dict(
|
|
533
|
+
values=['sectionslist', ],
|
|
534
|
+
),
|
|
535
|
+
task = dict(
|
|
536
|
+
optional = True,
|
|
537
|
+
default = 'anonymous'
|
|
538
|
+
),
|
|
539
|
+
clscontents = dict(
|
|
540
|
+
default = SectionsJsonListContent,
|
|
541
|
+
),
|
|
542
|
+
nativefmt = dict(
|
|
543
|
+
values = ['json', ],
|
|
544
|
+
default = 'json',
|
|
545
|
+
)
|
|
546
|
+
)
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
@property
|
|
550
|
+
def realkind(self):
|
|
551
|
+
return "sectionslist"
|