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/data/contents.py
ADDED
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A handful of abstract and generic :class:`DataContent` classes designed to access
|
|
3
|
+
and modify data of a given "Resource".
|
|
4
|
+
|
|
5
|
+
These classes are not meant to be used directly. To retrieve a
|
|
6
|
+
:class:`DataContent` object on a given "Resource", please use the
|
|
7
|
+
:data:`vortex.data.handlers.Handler.contents` property.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import collections
|
|
11
|
+
from string import Template
|
|
12
|
+
|
|
13
|
+
from bronx.fancies import loggers
|
|
14
|
+
from bronx.stdtypes.dictionaries import ReadOnlyDict
|
|
15
|
+
from bronx.stdtypes.xtemplates import DefaultTemplate
|
|
16
|
+
from bronx.syntax.decorators import secure_getattr
|
|
17
|
+
import footprints
|
|
18
|
+
|
|
19
|
+
from vortex import sessions
|
|
20
|
+
|
|
21
|
+
#: No automatic export
|
|
22
|
+
__all__ = []
|
|
23
|
+
|
|
24
|
+
logger = loggers.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DataContentError(ValueError):
|
|
28
|
+
"""General content error."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DataContent:
|
|
33
|
+
"""Root class for data contents used by resources."""
|
|
34
|
+
|
|
35
|
+
_diffable = False
|
|
36
|
+
|
|
37
|
+
def __init__(self, **kw):
|
|
38
|
+
self._datafmt = None
|
|
39
|
+
self._data = None
|
|
40
|
+
self._metadata = ReadOnlyDict()
|
|
41
|
+
self._size = 0
|
|
42
|
+
for k, v in kw.items():
|
|
43
|
+
self.__dict__['_' + k] = v
|
|
44
|
+
|
|
45
|
+
@secure_getattr
|
|
46
|
+
def __getattr__(self, attr):
|
|
47
|
+
"""Forward get attribute request to internal data object."""
|
|
48
|
+
if attr not in ('__getstate__', '__deepcopy__'):
|
|
49
|
+
return getattr(self.data, attr)
|
|
50
|
+
else:
|
|
51
|
+
raise AttributeError(attr)
|
|
52
|
+
|
|
53
|
+
def __enter__(self):
|
|
54
|
+
"""Enter a :keyword:`with` context."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
58
|
+
"""Exit from :keyword:`with` context."""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def data(self):
|
|
63
|
+
"""The internal data encapsulated."""
|
|
64
|
+
return self._data
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def size(self):
|
|
68
|
+
"""The actual size of the contents."""
|
|
69
|
+
return self._size
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def metadata(self):
|
|
73
|
+
"""Return the metadata of the ressource in the container (maybe empty)."""
|
|
74
|
+
return self._metadata
|
|
75
|
+
|
|
76
|
+
def metadata_check(self, resource, delta=None):
|
|
77
|
+
"""Check that the metadata of the resource in the container matches
|
|
78
|
+
the attributes of the resource given as an argument.
|
|
79
|
+
|
|
80
|
+
Prior to the comparison, the delta argument will be added to the
|
|
81
|
+
attribute read in the container. For example, if delta=dict(date='-PT1H'),
|
|
82
|
+
we will in fact check that the date of the resource in the container is
|
|
83
|
+
one hour before the date specified in the resource's footprint.
|
|
84
|
+
"""
|
|
85
|
+
if not len(self.metadata):
|
|
86
|
+
logger.error('Metadata check is not implemented for this format. ' +
|
|
87
|
+
'The check will always succeed...')
|
|
88
|
+
delta = delta or {}
|
|
89
|
+
outcome = True
|
|
90
|
+
for mkey, mval in self.metadata.items():
|
|
91
|
+
if hasattr(resource, mkey):
|
|
92
|
+
cval = getattr(resource, mkey)
|
|
93
|
+
if mkey in delta:
|
|
94
|
+
cval += delta[mkey]
|
|
95
|
+
outcome = outcome and cval == mval
|
|
96
|
+
if not outcome:
|
|
97
|
+
logger.warning("The ressource in the container doesn't match the resource footprint: %s",
|
|
98
|
+
str(self.metadata))
|
|
99
|
+
return outcome
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def datafmt(self):
|
|
103
|
+
"""The initial format of the contents."""
|
|
104
|
+
return self._datafmt
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def export_dict(cls):
|
|
108
|
+
"""Return current class name for shell or pure dict export mechanism."""
|
|
109
|
+
return (cls.__module__, cls.__name__)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def updated(self):
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
def slurp(self, container):
|
|
116
|
+
"""Should be overwritten. Basically get the totalsize of the actual container."""
|
|
117
|
+
self._size = container.totalsize
|
|
118
|
+
|
|
119
|
+
def _merge_checkclass(self, *kargs):
|
|
120
|
+
"""Utility method to check that all the kargs objects are compatible self."""
|
|
121
|
+
if not all([isinstance(obj, self.__class__) for obj in kargs]):
|
|
122
|
+
raise DataContentError("The object's types are not compatible with self")
|
|
123
|
+
|
|
124
|
+
def merge(self, *kargs):
|
|
125
|
+
"""Merge several DataContents into one.
|
|
126
|
+
|
|
127
|
+
This method have to be implemented and _merge_checkclass should be called
|
|
128
|
+
to ensure that the object's types are compatible with self.
|
|
129
|
+
"""
|
|
130
|
+
raise NotImplementedError("Merge is not implemented for this content.")
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def is_diffable(cls):
|
|
134
|
+
"""Is the diff operation implemented for this content class ?"""
|
|
135
|
+
return cls._diffable
|
|
136
|
+
|
|
137
|
+
def diff(self, ref):
|
|
138
|
+
"""Compare the present content with the ``ref`` content."""
|
|
139
|
+
if not self.is_diffable():
|
|
140
|
+
raise NotImplementedError("Diff is not implemented for this content")
|
|
141
|
+
else:
|
|
142
|
+
return self._actual_diff(ref)
|
|
143
|
+
|
|
144
|
+
def _actual_diff(self, ref):
|
|
145
|
+
"""A very simple kind of comparison... but it might work !"""
|
|
146
|
+
return self.data == ref.data
|
|
147
|
+
|
|
148
|
+
def rewrite(self, container):
|
|
149
|
+
"""Abstract method."""
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class UnknownContent(DataContent):
|
|
154
|
+
"""Fake DataContent subclass."""
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class AlmostDictContent(DataContent):
|
|
159
|
+
"""Implement some dictionary-like functions."""
|
|
160
|
+
|
|
161
|
+
# The very simple diff method form DataContent should do the job.
|
|
162
|
+
_diffable = True
|
|
163
|
+
|
|
164
|
+
def __init__(self, **kw):
|
|
165
|
+
super().__init__(**kw)
|
|
166
|
+
if self._data is None:
|
|
167
|
+
self._data = dict()
|
|
168
|
+
|
|
169
|
+
def fmtkey(self, key):
|
|
170
|
+
"""Reshape entry keys of the internal dictionary."""
|
|
171
|
+
return key
|
|
172
|
+
|
|
173
|
+
def __getitem__(self, idx):
|
|
174
|
+
return self._data[self.fmtkey(idx)]
|
|
175
|
+
|
|
176
|
+
def __setitem__(self, idx, value):
|
|
177
|
+
self._data[self.fmtkey(idx)] = value
|
|
178
|
+
|
|
179
|
+
def __delitem__(self, idx):
|
|
180
|
+
del self._data[self.fmtkey(idx)]
|
|
181
|
+
|
|
182
|
+
def __len__(self):
|
|
183
|
+
return len(self._data)
|
|
184
|
+
|
|
185
|
+
def __iter__(self):
|
|
186
|
+
yield from self._data.keys()
|
|
187
|
+
|
|
188
|
+
def __contains__(self, item):
|
|
189
|
+
return self.fmtkey(item) in self._data
|
|
190
|
+
|
|
191
|
+
def has_key(self, item):
|
|
192
|
+
"""Dict-like behavior looking for the formatted ``item`` in internal data."""
|
|
193
|
+
return item in self
|
|
194
|
+
|
|
195
|
+
def merge(self, *kargs):
|
|
196
|
+
"""Merge several data contents into one."""
|
|
197
|
+
self._merge_checkclass(*kargs)
|
|
198
|
+
for obj in kargs:
|
|
199
|
+
self._data.update(obj.data)
|
|
200
|
+
self._size += obj.size
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class IndexedTable(AlmostDictContent):
|
|
204
|
+
"""
|
|
205
|
+
Multi-columns table indexed by first column.
|
|
206
|
+
Behaves mostly as a dictionary.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def append(self, item):
|
|
210
|
+
"""Insert data according to index position given as the first element of the ``item`` list."""
|
|
211
|
+
if len(item) > 0:
|
|
212
|
+
i = item.pop(0)
|
|
213
|
+
self._data[self.fmtkey(i)] = item
|
|
214
|
+
|
|
215
|
+
def extend(self, addlist):
|
|
216
|
+
"""Insert data according to index position given as the first item of ``addlist``."""
|
|
217
|
+
for idxinput in addlist:
|
|
218
|
+
self.append(idxinput)
|
|
219
|
+
|
|
220
|
+
def slurp(self, container):
|
|
221
|
+
"""Get data from the ``container``."""
|
|
222
|
+
with container.preferred_decoding(byte=False):
|
|
223
|
+
container.rewind()
|
|
224
|
+
self.extend([x.split() for x in container.readlines() if not x.startswith('#')])
|
|
225
|
+
self._size = container.totalsize
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class JsonDictContent(AlmostDictContent):
|
|
229
|
+
"""
|
|
230
|
+
The internal data is supposed to be read from a json file.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
def __init__(self, **kw):
|
|
234
|
+
self._bronx_tpl = None
|
|
235
|
+
super().__init__(** kw)
|
|
236
|
+
|
|
237
|
+
def slurp(self, container):
|
|
238
|
+
"""Get data from the ``container``."""
|
|
239
|
+
t = sessions.current()
|
|
240
|
+
with container.preferred_decoding(byte=False):
|
|
241
|
+
container.rewind()
|
|
242
|
+
self._data = t.sh.json_load(container.iotarget())
|
|
243
|
+
self._size = container.totalsize
|
|
244
|
+
|
|
245
|
+
def bronx_tpl_render(self, **kwargs):
|
|
246
|
+
"""Use :mod:`bronx.stdtypes.xtemplates` to render a JSON template."""
|
|
247
|
+
if self._bronx_tpl is None:
|
|
248
|
+
# Freeze the original data
|
|
249
|
+
self._bronx_tpl = self._data
|
|
250
|
+
# Start rendering and overwrite data
|
|
251
|
+
dt = DefaultTemplate(self._bronx_tpl)
|
|
252
|
+
self._data = dt.render(**kwargs)
|
|
253
|
+
|
|
254
|
+
def rewrite(self, container):
|
|
255
|
+
"""Write the list contents in the specified container."""
|
|
256
|
+
t = sessions.current()
|
|
257
|
+
container.close()
|
|
258
|
+
# In Python 2, json.dumps returns 'str', not unicode...
|
|
259
|
+
with container.iod_context():
|
|
260
|
+
with container.preferred_decoding(byte=False):
|
|
261
|
+
with container.preferred_write():
|
|
262
|
+
iod = container.iodesc()
|
|
263
|
+
t.sh.json_dump(self.data, iod, indent=4)
|
|
264
|
+
container.updfill(True)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class AlmostListContent(DataContent):
|
|
268
|
+
"""
|
|
269
|
+
Implement some list-like functions.
|
|
270
|
+
The argument maxprint is used for the maximum number of lines
|
|
271
|
+
to display through the str function.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
# The very simple diff method form DataContent should do the job.
|
|
275
|
+
_diffable = True
|
|
276
|
+
|
|
277
|
+
def __init__(self, **kw):
|
|
278
|
+
self._maxprint = kw.pop('maxprint', 20)
|
|
279
|
+
super().__init__(**kw)
|
|
280
|
+
if self._data is None:
|
|
281
|
+
self._data = list()
|
|
282
|
+
|
|
283
|
+
def __delitem__(self, idx):
|
|
284
|
+
del self.data[idx]
|
|
285
|
+
|
|
286
|
+
def __delslice__(self, istart, iend):
|
|
287
|
+
del self.data[istart:iend]
|
|
288
|
+
|
|
289
|
+
def __setitem__(self, idx, value):
|
|
290
|
+
self.data[idx] = value
|
|
291
|
+
|
|
292
|
+
def __setslice__(self, istart, iend, value):
|
|
293
|
+
self.data[istart:iend] = value
|
|
294
|
+
|
|
295
|
+
def __getitem__(self, idx):
|
|
296
|
+
return self.data[idx]
|
|
297
|
+
|
|
298
|
+
def __getslice__(self, istart, iend):
|
|
299
|
+
return self.data[istart:iend]
|
|
300
|
+
|
|
301
|
+
def __sizeof__(self):
|
|
302
|
+
return self.data.__sizeof__()
|
|
303
|
+
|
|
304
|
+
def __len__(self):
|
|
305
|
+
return len(self.data)
|
|
306
|
+
|
|
307
|
+
def __iter__(self):
|
|
308
|
+
yield from self.data
|
|
309
|
+
|
|
310
|
+
def __call__(self):
|
|
311
|
+
return self.data
|
|
312
|
+
|
|
313
|
+
def _get_maxprint(self):
|
|
314
|
+
return self._maxprint
|
|
315
|
+
|
|
316
|
+
def _set_maxprint(self, value):
|
|
317
|
+
try:
|
|
318
|
+
self._maxprint = abs(int(value))
|
|
319
|
+
except (ValueError, TypeError):
|
|
320
|
+
pass
|
|
321
|
+
self._maxprint = max(10, self._maxprint)
|
|
322
|
+
|
|
323
|
+
maxprint = property(_get_maxprint, _set_maxprint, None)
|
|
324
|
+
|
|
325
|
+
def clear(self):
|
|
326
|
+
"""Clear all internal data contents."""
|
|
327
|
+
self._data[:] = []
|
|
328
|
+
|
|
329
|
+
def slurp(self, container):
|
|
330
|
+
"""Get data from the ``container``."""
|
|
331
|
+
with container.preferred_decoding(byte=False):
|
|
332
|
+
self._data.extend(container.readlines())
|
|
333
|
+
self._size = container.totalsize
|
|
334
|
+
|
|
335
|
+
def rewrite(self, container):
|
|
336
|
+
"""Write the list contents in the specified container."""
|
|
337
|
+
container.close()
|
|
338
|
+
with container.iod_context():
|
|
339
|
+
with container.preferred_decoding(byte=False):
|
|
340
|
+
for xline in self:
|
|
341
|
+
container.write(xline)
|
|
342
|
+
|
|
343
|
+
def sort(self, **sort_opts):
|
|
344
|
+
"""Sort the current object."""
|
|
345
|
+
self._data.sort(**sort_opts)
|
|
346
|
+
|
|
347
|
+
def merge(self, *kargs, **kwargs):
|
|
348
|
+
"""Merge several data contents into one."""
|
|
349
|
+
unique = kwargs.get('unique', False)
|
|
350
|
+
self._merge_checkclass(*kargs)
|
|
351
|
+
for obj in kargs:
|
|
352
|
+
self.data.extend(obj.data)
|
|
353
|
+
self._size += obj.size
|
|
354
|
+
# Check if the item are unique, raise an error if not (option unique = True)
|
|
355
|
+
if unique:
|
|
356
|
+
arg_elements = collections.Counter(self.data)
|
|
357
|
+
repeated_elements = [element for element, count in arg_elements.items() if count > 1]
|
|
358
|
+
if len(repeated_elements) > 0:
|
|
359
|
+
logger.exception('Repeated argument are present. It should not. Stop.' +
|
|
360
|
+
'The list of the repeated elements follows: %s',
|
|
361
|
+
str(sorted(repeated_elements)))
|
|
362
|
+
raise DataContentError('Repeated argument are present. It should not.')
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class TextContent(AlmostListContent):
|
|
366
|
+
"""
|
|
367
|
+
Multi-lines input text data split through blank seperator.
|
|
368
|
+
Behaves mostly as a list.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
def __init__(self, **kw):
|
|
372
|
+
kw.setdefault('fmt', None)
|
|
373
|
+
super().__init__(**kw)
|
|
374
|
+
|
|
375
|
+
def __str__(self):
|
|
376
|
+
if len(self) > self.maxprint:
|
|
377
|
+
catlist = self[0:3] + ['...'] + self[-3:]
|
|
378
|
+
else:
|
|
379
|
+
catlist = self[:]
|
|
380
|
+
return '\n'.join([str(x) for x in catlist])
|
|
381
|
+
|
|
382
|
+
def slurp(self, container):
|
|
383
|
+
with container.preferred_decoding(byte=False):
|
|
384
|
+
self._data.extend([x.split() for x in container if not x.startswith('#')])
|
|
385
|
+
self._size = container.totalsize
|
|
386
|
+
|
|
387
|
+
def formatted_data(self, item):
|
|
388
|
+
"""Return a formatted string according to optional internal fmt."""
|
|
389
|
+
if self._fmt is None:
|
|
390
|
+
return ' '.join([str(x) for x in item])
|
|
391
|
+
else:
|
|
392
|
+
return self._fmt.format(*item)
|
|
393
|
+
|
|
394
|
+
def rewrite(self, container):
|
|
395
|
+
"""Write the text contents in the specified container."""
|
|
396
|
+
container.close()
|
|
397
|
+
with container.iod_context():
|
|
398
|
+
with container.preferred_decoding(byte=False):
|
|
399
|
+
for item in self:
|
|
400
|
+
container.write(self.formatted_data(item) + '\n')
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class DataRaw(AlmostListContent):
|
|
404
|
+
"""
|
|
405
|
+
Multi-lines raw data (no format assumed).
|
|
406
|
+
Behaves mostly as a list.
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
def __init__(self, data=None, window=0, datafmt=None):
|
|
410
|
+
if not data and window:
|
|
411
|
+
data = collections.deque(maxlen=window)
|
|
412
|
+
super().__init__(data=data, window=window, datafmt=datafmt)
|
|
413
|
+
|
|
414
|
+
def slurp(self, container):
|
|
415
|
+
with container.preferred_decoding(byte=False):
|
|
416
|
+
container.rewind()
|
|
417
|
+
end = False
|
|
418
|
+
while not end:
|
|
419
|
+
data, end = container.dataread()
|
|
420
|
+
self._data.append(data)
|
|
421
|
+
if self._window and len(self._data) >= self._window:
|
|
422
|
+
end = True
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class DataTemplate(DataContent):
|
|
426
|
+
"""
|
|
427
|
+
Multi-lines data which fits to a template.
|
|
428
|
+
Behave mostly as a list.
|
|
429
|
+
"""
|
|
430
|
+
|
|
431
|
+
def slurp(self, container):
|
|
432
|
+
"""Actually read a container."""
|
|
433
|
+
with container.preferred_decoding(byte=False):
|
|
434
|
+
container.rewind()
|
|
435
|
+
self._data = container.read()
|
|
436
|
+
super().slurp(container)
|
|
437
|
+
|
|
438
|
+
def setitems(self, keyvaluedict):
|
|
439
|
+
"""
|
|
440
|
+
Substitute the different keys contained in a dictionary into
|
|
441
|
+
the data content using a template.
|
|
442
|
+
|
|
443
|
+
:param dict keyvaluedict: things to be substituted.
|
|
444
|
+
"""
|
|
445
|
+
data_tmp = Template(self._data)
|
|
446
|
+
self._data = data_tmp.substitute(keyvaluedict)
|
|
447
|
+
|
|
448
|
+
def rewrite(self, container):
|
|
449
|
+
"""Write the list contents in the specified container."""
|
|
450
|
+
container.close()
|
|
451
|
+
with container.iod_context():
|
|
452
|
+
with container.preferred_decoding(byte=False):
|
|
453
|
+
container.write(self.data)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class FormatAdapter(DataContent):
|
|
457
|
+
"""Adapter to objects that could manage a dedicated format."""
|
|
458
|
+
|
|
459
|
+
def __init__(self, **kw):
|
|
460
|
+
super().__init__(**kw)
|
|
461
|
+
if self._data is None and footprints.proxy.dataformats is None:
|
|
462
|
+
logger.warning('No collector for data formats')
|
|
463
|
+
self._datafmt = None
|
|
464
|
+
|
|
465
|
+
def __enter__(self):
|
|
466
|
+
"""
|
|
467
|
+
Enter a :keyword:`with` context, setting some FORTRAN variables
|
|
468
|
+
in order to switch off parallelism (in case of execution).
|
|
469
|
+
"""
|
|
470
|
+
t = sessions.current()
|
|
471
|
+
t.env.delta(
|
|
472
|
+
LFI_HNDL_SPEC=':1',
|
|
473
|
+
DR_HOOK_SILENT=1,
|
|
474
|
+
DR_HOOK_NOT_MPI=1,
|
|
475
|
+
OMP_NUM_THREADS=1,
|
|
476
|
+
)
|
|
477
|
+
return self
|
|
478
|
+
|
|
479
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
480
|
+
"""Exit from :keyword:`with` context."""
|
|
481
|
+
t = sessions.current()
|
|
482
|
+
t.env.rewind()
|
|
483
|
+
|
|
484
|
+
def slurp(self, container):
|
|
485
|
+
"""Load a dataformat object."""
|
|
486
|
+
if self.datafmt:
|
|
487
|
+
with self:
|
|
488
|
+
self._data = footprints.proxy.dataformat(
|
|
489
|
+
filename=container.abspath,
|
|
490
|
+
openmode='r',
|
|
491
|
+
fmtdelayedopen=True,
|
|
492
|
+
format=container.actualfmt.upper(),
|
|
493
|
+
)
|
|
494
|
+
# Look for a metadatareader object
|
|
495
|
+
if self._data is not None and footprints.proxy.metadatareaders is not None:
|
|
496
|
+
mreader = footprints.proxy.metadatareader(
|
|
497
|
+
format=container.actualfmt.upper(),
|
|
498
|
+
_emptywarning=False,
|
|
499
|
+
)
|
|
500
|
+
if mreader is not None:
|
|
501
|
+
mreader.content_init(self._data)
|
|
502
|
+
self._metadata = mreader
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class MetaDataReader(footprints.FootprintBase):
|
|
506
|
+
"""
|
|
507
|
+
Abstract class for any MetaDataReader.
|
|
508
|
+
|
|
509
|
+
Note: _do_delayed_init have to be subclassed. That's where the content of the
|
|
510
|
+
container is actually read.
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
_abstract = True
|
|
514
|
+
_collector = ('metadatareader',)
|
|
515
|
+
_footprint = dict(
|
|
516
|
+
info = 'Abstract MetaDataReader',
|
|
517
|
+
attr = dict(
|
|
518
|
+
format = dict(
|
|
519
|
+
type = str,
|
|
520
|
+
)
|
|
521
|
+
)
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def __init__(self, *kargs, **kwargs):
|
|
525
|
+
self._content_in = None
|
|
526
|
+
self._datahide = None
|
|
527
|
+
super().__init__(*kargs, **kwargs)
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def _data(self):
|
|
531
|
+
"""Internal: check if one needs to intialise the _datahide dict."""
|
|
532
|
+
if self._datahide is None and self._content_in is not None:
|
|
533
|
+
self._do_delayed_init()
|
|
534
|
+
return self._datahide
|
|
535
|
+
|
|
536
|
+
def content_init(self, thecontent):
|
|
537
|
+
"""Set the data content that will be used to read the metadata"""
|
|
538
|
+
self._content_in = thecontent
|
|
539
|
+
|
|
540
|
+
def _do_delayed_init(self):
|
|
541
|
+
"""Internal: actually initialise the _data array. Have to be subclassed !"""
|
|
542
|
+
raise NotImplementedError
|
|
543
|
+
|
|
544
|
+
def __getitem__(self, key):
|
|
545
|
+
return self._data[key]
|
|
546
|
+
|
|
547
|
+
def __len__(self):
|
|
548
|
+
return len(self._data)
|
|
549
|
+
|
|
550
|
+
def __iter__(self):
|
|
551
|
+
return iter(self._data)
|
|
552
|
+
|
|
553
|
+
def items(self):
|
|
554
|
+
"""Iterate over the metadata."""
|
|
555
|
+
for k in self:
|
|
556
|
+
yield k, self[k]
|
|
557
|
+
|
|
558
|
+
def __repr__(self):
|
|
559
|
+
if self._datahide is None:
|
|
560
|
+
return '{}: Not yet initialised'.format(self.__class__)
|
|
561
|
+
else:
|
|
562
|
+
return repr(self._data)
|
|
563
|
+
|
|
564
|
+
def __str__(self):
|
|
565
|
+
return str(self._data)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
class FormatAdapterAbstractImplementation(footprints.FootprintBase):
|
|
569
|
+
"""
|
|
570
|
+
The minimal set of attributes needed to create a class that is compatible
|
|
571
|
+
with the :class:`FormatAdapter` content.
|
|
572
|
+
"""
|
|
573
|
+
|
|
574
|
+
_abstract = True
|
|
575
|
+
_collector = ('dataformat',)
|
|
576
|
+
_footprint = dict(
|
|
577
|
+
attr=dict(
|
|
578
|
+
filename=dict(
|
|
579
|
+
info="Path to the target data.",
|
|
580
|
+
),
|
|
581
|
+
openmode=dict(
|
|
582
|
+
info="File open-mode.",
|
|
583
|
+
values=['r', 'rw'],
|
|
584
|
+
default='r',
|
|
585
|
+
optional=True,
|
|
586
|
+
),
|
|
587
|
+
fmtdelayedopen=dict(
|
|
588
|
+
info="Delay the opening of the listing file.",
|
|
589
|
+
type=bool,
|
|
590
|
+
default=True,
|
|
591
|
+
optional=True,
|
|
592
|
+
),
|
|
593
|
+
format=dict(
|
|
594
|
+
),
|
|
595
|
+
)
|
|
596
|
+
)
|