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/toolbox.py
ADDED
|
@@ -0,0 +1,982 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Top level interface for accessing the VORTEX facilities.
|
|
3
|
+
|
|
4
|
+
This module does not provides any class, constant, or any nice object.
|
|
5
|
+
It defines a very basic interface to some (possibly) powerful capacities
|
|
6
|
+
of the :mod:`vortex` toolbox.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from contextlib import contextmanager
|
|
10
|
+
import re
|
|
11
|
+
import traceback
|
|
12
|
+
|
|
13
|
+
from bronx.fancies import loggers
|
|
14
|
+
from bronx.stdtypes.history import History
|
|
15
|
+
from bronx.syntax import mktuple
|
|
16
|
+
|
|
17
|
+
import footprints
|
|
18
|
+
|
|
19
|
+
from vortex import sessions, data, proxy, VortexForceComplete
|
|
20
|
+
from vortex.layout.dataflow import stripargs_section, intent, ixo, Section
|
|
21
|
+
|
|
22
|
+
#: Automatic export of superstar interface.
|
|
23
|
+
__all__ = ['rload', 'rget', 'rput']
|
|
24
|
+
|
|
25
|
+
logger = loggers.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
#: Shortcut to footprint env defaults
|
|
28
|
+
defaults = footprints.setup.defaults
|
|
29
|
+
|
|
30
|
+
sectionmap = {'input': 'get', 'output': 'put', 'executable': 'get'}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Toolbox defaults
|
|
34
|
+
|
|
35
|
+
#: Default value for the **now** attribute of :func:`input`, :func:`executable`
|
|
36
|
+
#: and :func:`output` functions
|
|
37
|
+
active_now = False
|
|
38
|
+
#: Default value for the **insitu** attribute of the :func:`input` and
|
|
39
|
+
#: :func:`executable` functions
|
|
40
|
+
active_insitu = False
|
|
41
|
+
#: If *False*, drastically reduces the amount of messages printed by the
|
|
42
|
+
#: toolbox module
|
|
43
|
+
active_verbose = True
|
|
44
|
+
#: If *False*, do not try to create/make any promise
|
|
45
|
+
active_promise = True
|
|
46
|
+
#: If *False*, this makes the :func:`clear_promises` function inactive
|
|
47
|
+
active_clear = False
|
|
48
|
+
#: If *False*, this will reset to *False* any ``metadatacheck`` attribute
|
|
49
|
+
#: passed to the :func:`input` or :func:`executable` functions
|
|
50
|
+
active_metadatacheck = True
|
|
51
|
+
#: If *True*, archive stores will not be used at all (only cache stores will
|
|
52
|
+
#: be used)
|
|
53
|
+
active_incache = False
|
|
54
|
+
#: Use the earlyget feature during :func:`input` calls
|
|
55
|
+
active_batchinputs = True
|
|
56
|
+
|
|
57
|
+
#: History recording
|
|
58
|
+
history = History(tag='rload')
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# Most commonly used functions
|
|
62
|
+
|
|
63
|
+
def show_toolbox_settings(ljust=24):
|
|
64
|
+
"""Print the current settings of the toolbox."""
|
|
65
|
+
for key in ['active_{}'.format(act) for act in
|
|
66
|
+
('now', 'insitu', 'verbose', 'promise', 'clear',
|
|
67
|
+
'metadatacheck', 'incache')]:
|
|
68
|
+
kval = globals().get(key, None)
|
|
69
|
+
if kval is not None:
|
|
70
|
+
print('+', key.ljust(ljust), '=', kval)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def quickview(args, nb=0, indent=0):
|
|
74
|
+
"""Recursive call to any quick view of objects specified as arguments."""
|
|
75
|
+
if not isinstance(args, list) and not isinstance(args, tuple):
|
|
76
|
+
args = (args, )
|
|
77
|
+
for x in args:
|
|
78
|
+
if nb:
|
|
79
|
+
print()
|
|
80
|
+
nb += 1
|
|
81
|
+
quickview = getattr(x, 'quickview', None)
|
|
82
|
+
if quickview:
|
|
83
|
+
quickview(nb, indent)
|
|
84
|
+
else:
|
|
85
|
+
print('{:02d}. {:s}'.format(nb, x))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class VortexToolboxDescError(Exception):
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def rload(*args, **kw):
|
|
93
|
+
"""
|
|
94
|
+
Resource Loader.
|
|
95
|
+
|
|
96
|
+
This function behaves as a factory for any possible pre-defined family
|
|
97
|
+
of VORTEX resources (described by an aggregation of Resource, Provider and
|
|
98
|
+
Container objects).
|
|
99
|
+
|
|
100
|
+
Arguments could be a mix of a list of dictionary-type objects and key/value
|
|
101
|
+
parameters. Other type of arguments will be discarded.
|
|
102
|
+
|
|
103
|
+
An abstract resource descriptor is built as the aggregation of these
|
|
104
|
+
arguments and then expanded according to rules defined by the
|
|
105
|
+
:func:`footprints.util.expand` function.
|
|
106
|
+
|
|
107
|
+
For each expanded descriptor, the ``rload`` method will try to pickup the
|
|
108
|
+
best candidates (if any) that could match the description (*i.e.* Resource,
|
|
109
|
+
Provider, Container). If no match is found for one of the Resource, Provider
|
|
110
|
+
or Container objects, a :class:`VortexToolboxDescError` exception is raised.
|
|
111
|
+
Otherwise,the resource's :class:`~vortex.data.handlers.Handler` built from
|
|
112
|
+
those three objects is added to the result's list.
|
|
113
|
+
|
|
114
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects.
|
|
115
|
+
"""
|
|
116
|
+
rd = dict()
|
|
117
|
+
for a in args:
|
|
118
|
+
if isinstance(a, dict):
|
|
119
|
+
rd.update(a)
|
|
120
|
+
else:
|
|
121
|
+
logger.warning('Discard rload argument <%s>', a)
|
|
122
|
+
rd.update(kw)
|
|
123
|
+
if rd:
|
|
124
|
+
history.append(rd.copy())
|
|
125
|
+
rhx = []
|
|
126
|
+
for x in footprints.util.expand(rd):
|
|
127
|
+
picked_up = proxy.containers.pickup( # @UndefinedVariable
|
|
128
|
+
* proxy.providers.pickup_and_cache( # @UndefinedVariable
|
|
129
|
+
* proxy.resources.pickup_and_cache(x) # @UndefinedVariable
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
logger.debug('Resource desc %s', picked_up)
|
|
133
|
+
picked_rh = data.handlers.Handler(picked_up)
|
|
134
|
+
if not picked_rh.complete:
|
|
135
|
+
raise VortexToolboxDescError("The ResourceHandler is incomplete")
|
|
136
|
+
rhx.append(picked_rh)
|
|
137
|
+
|
|
138
|
+
return rhx
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def rh(*args, **kw):
|
|
142
|
+
"""
|
|
143
|
+
This function selects the first resource's handler as returned by the
|
|
144
|
+
:func:`rload` function.
|
|
145
|
+
"""
|
|
146
|
+
return rload(*args, **kw)[0]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def rget(*args, **kw):
|
|
150
|
+
"""
|
|
151
|
+
This function calls the :meth:`get` method on any resource handler returned
|
|
152
|
+
by the :func:`rload` function.
|
|
153
|
+
"""
|
|
154
|
+
loc_incache = kw.pop('incache', active_incache)
|
|
155
|
+
rl = rload(*args, **kw)
|
|
156
|
+
for rh in rl:
|
|
157
|
+
rh.get(incache=loc_incache)
|
|
158
|
+
return rl
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def rput(*args, **kw):
|
|
162
|
+
"""
|
|
163
|
+
This function calls the :meth:`put` method on any resource handler returned
|
|
164
|
+
by the :func:`rload` function.
|
|
165
|
+
"""
|
|
166
|
+
loc_incache = kw.pop('incache', active_incache)
|
|
167
|
+
rl = rload(*args, **kw)
|
|
168
|
+
for rh in rl:
|
|
169
|
+
rh.put(incache=loc_incache)
|
|
170
|
+
return rl
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def nicedump(msg, **kw):
|
|
174
|
+
"""Simple dump the **kw** dict content with ``msg`` as header."""
|
|
175
|
+
print('#', msg, ':')
|
|
176
|
+
for k, v in sorted(kw.items()):
|
|
177
|
+
print('+', k.ljust(12), '=', str(v))
|
|
178
|
+
print()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@contextmanager
|
|
182
|
+
def _tb_isolate(t, loglevel):
|
|
183
|
+
"""Handle the context and logger (internal use only)."""
|
|
184
|
+
# Switch off autorecording of the current context
|
|
185
|
+
ctx = t.context
|
|
186
|
+
recordswitch = ctx.record
|
|
187
|
+
if recordswitch:
|
|
188
|
+
ctx.record_off()
|
|
189
|
+
try:
|
|
190
|
+
if loglevel is not None:
|
|
191
|
+
# Possibly change the log level if necessary
|
|
192
|
+
with loggers.contextboundGlobalLevel(loglevel):
|
|
193
|
+
yield
|
|
194
|
+
else:
|
|
195
|
+
yield
|
|
196
|
+
finally:
|
|
197
|
+
if recordswitch:
|
|
198
|
+
ctx.record_on()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def add_section(section, args, kw):
|
|
202
|
+
"""
|
|
203
|
+
Add a :class:`~vortex.layout.dataflow.Section` object (of kind **section**)
|
|
204
|
+
to the current sequence.
|
|
205
|
+
|
|
206
|
+
1. The **kw** dictionary may contain keys that influence this function
|
|
207
|
+
behaviour (such attributes are popped from **kw** before going further):
|
|
208
|
+
|
|
209
|
+
* **now**: If *True*, call the appropriate action (``get()`` or ``put()``)
|
|
210
|
+
on each added :class:`~vortex.layout.dataflow.Section`. (The default
|
|
211
|
+
is given by :data:`active_now`).
|
|
212
|
+
* **loglevel**: The logging facility verbosity level that will be used
|
|
213
|
+
during the :class:`~vortex.layout.dataflow.Section` creation process.
|
|
214
|
+
If *None*, nothing is done (i.e. the current verbosity level is
|
|
215
|
+
preserved). (default: *None*).
|
|
216
|
+
* **verbose**: If *True*, print some informations on the standard output
|
|
217
|
+
(The default is given by :data:`active_verbose`).
|
|
218
|
+
* **complete**: If *True*, force the task to complete (the
|
|
219
|
+
:class:`VortexForceComplete` exception is raised) whenever an error
|
|
220
|
+
occurs. (default: False).
|
|
221
|
+
* **insitu**: It *True*, before actually getting data, we first check
|
|
222
|
+
if the data is already there (in the context of a multi-step job,
|
|
223
|
+
it might have been fetched during a previous step). (default: *False*).
|
|
224
|
+
* **incache**: It *True*, archive stores will not be used at all (only cache
|
|
225
|
+
stores will be used). (The default is given by :data:`active_incache`).
|
|
226
|
+
|
|
227
|
+
2. **kw** is then looked for items relevant to the
|
|
228
|
+
:class:`~vortex.layout.dataflow.Section` constructor (``role``, ``intent``,
|
|
229
|
+
...). (such items are popped from kw before going further).
|
|
230
|
+
|
|
231
|
+
3. The remaining **kw** items are passed directly to the :func:`rload`
|
|
232
|
+
function in order to create the resource's
|
|
233
|
+
:class:`~vortex.data.handlers.Handler`.
|
|
234
|
+
|
|
235
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
t = sessions.current()
|
|
239
|
+
|
|
240
|
+
# First, retrieve arguments of the toolbox command itself
|
|
241
|
+
now = kw.pop('now', active_now)
|
|
242
|
+
loglevel = kw.pop('loglevel', None)
|
|
243
|
+
talkative = kw.pop('verbose', active_verbose)
|
|
244
|
+
complete = kw.pop('complete', False)
|
|
245
|
+
insitu = kw.get('insitu', False)
|
|
246
|
+
batch = kw.pop('batch', False)
|
|
247
|
+
lastfatal = kw.pop('lastfatal', None)
|
|
248
|
+
|
|
249
|
+
if complete:
|
|
250
|
+
kw['fatal'] = False
|
|
251
|
+
|
|
252
|
+
if batch:
|
|
253
|
+
if section not in ('input', 'excutable'):
|
|
254
|
+
logger.info("batch=True is not implemented for section=%s. overwriting to batch=Fase.",
|
|
255
|
+
section)
|
|
256
|
+
batch = False
|
|
257
|
+
|
|
258
|
+
# Second, retrieve arguments that could be used by the now command
|
|
259
|
+
cmdopts = dict(
|
|
260
|
+
incache=kw.pop('incache', active_incache),
|
|
261
|
+
force=kw.pop('force', False)
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Third, collect arguments for triggering some hook
|
|
265
|
+
hooks = dict()
|
|
266
|
+
for ahook in [x for x in kw.keys() if x.startswith('hook_')]:
|
|
267
|
+
cbhook = mktuple(kw.pop(ahook))
|
|
268
|
+
cbfunc = cbhook[0]
|
|
269
|
+
if not callable(cbfunc):
|
|
270
|
+
cbfunc = t.sh.import_function(cbfunc)
|
|
271
|
+
hooks[ahook] = footprints.FPTuple((cbfunc, cbhook[1:]))
|
|
272
|
+
|
|
273
|
+
# Print the user inputs
|
|
274
|
+
def print_user_inputs():
|
|
275
|
+
nicedump('New {:s} section with options'.format(section), **opts)
|
|
276
|
+
nicedump('Resource handler description', **kwclean)
|
|
277
|
+
nicedump(
|
|
278
|
+
'This command options',
|
|
279
|
+
complete=complete,
|
|
280
|
+
loglevel=loglevel,
|
|
281
|
+
now=now,
|
|
282
|
+
verbose=talkative,
|
|
283
|
+
)
|
|
284
|
+
if hooks:
|
|
285
|
+
nicedump('Hooks triggered', **hooks)
|
|
286
|
+
|
|
287
|
+
with _tb_isolate(t, loglevel):
|
|
288
|
+
|
|
289
|
+
# Distinguish between section arguments, and resource loader arguments
|
|
290
|
+
opts, kwclean = stripargs_section(**kw)
|
|
291
|
+
|
|
292
|
+
# Strip the metadatacheck option depending on active_metadatacheck
|
|
293
|
+
if not active_metadatacheck and not insitu:
|
|
294
|
+
if kwclean.get('metadatacheck', False):
|
|
295
|
+
logger.info("The metadatacheck option is forced to False since " +
|
|
296
|
+
"active_metadatacheck=False.")
|
|
297
|
+
kwclean['metadatacheck'] = False
|
|
298
|
+
|
|
299
|
+
# Show the actual set of arguments
|
|
300
|
+
if talkative and not insitu:
|
|
301
|
+
print_user_inputs()
|
|
302
|
+
|
|
303
|
+
# Let the magic of footprints resolution operate...
|
|
304
|
+
kwclean.update(hooks)
|
|
305
|
+
rl = rload(*args, **kwclean)
|
|
306
|
+
rlok = list()
|
|
307
|
+
|
|
308
|
+
# Prepare the references to the actual section method to perform
|
|
309
|
+
push = getattr(t.context.sequence, section)
|
|
310
|
+
doitmethod = sectionmap[section]
|
|
311
|
+
|
|
312
|
+
# Create a section for each resource handler
|
|
313
|
+
if rl and lastfatal is not None:
|
|
314
|
+
newsections = [push(rh=rhandler, **opts)[0] for rhandler in rl[:-1]]
|
|
315
|
+
tmpopts = opts.copy()
|
|
316
|
+
tmpopts['fatal'] = lastfatal
|
|
317
|
+
newsections.append(push(rh=rl[-1], **tmpopts)[0])
|
|
318
|
+
else:
|
|
319
|
+
newsections = [push(rh=rhandler, **opts)[0] for rhandler in rl]
|
|
320
|
+
|
|
321
|
+
# If insitu and now, try a quiet get...
|
|
322
|
+
do_quick_insitu = section in ('input', 'executable') and insitu and now
|
|
323
|
+
if do_quick_insitu:
|
|
324
|
+
quickget = [sec.rh.insitu_quickget(alternate=sec.alternate, **cmdopts) for sec in newsections]
|
|
325
|
+
if all(quickget):
|
|
326
|
+
if len(quickget) > 1:
|
|
327
|
+
logger.info("The insitu get succeeded for all of the %d resource handlers.",
|
|
328
|
+
len(rl))
|
|
329
|
+
else:
|
|
330
|
+
logger.info("The insitu get succeeded for this resource handler.")
|
|
331
|
+
rlok = [sec.rh for sec in newsections]
|
|
332
|
+
else:
|
|
333
|
+
# Start again with the usual get sequence
|
|
334
|
+
print_user_inputs()
|
|
335
|
+
|
|
336
|
+
# If not insitu, not now, or if the quiet get failed
|
|
337
|
+
if not (do_quick_insitu and all(quickget)):
|
|
338
|
+
if now:
|
|
339
|
+
with t.sh.ftppool():
|
|
340
|
+
# Create a section for each resource handler, and perform action on demand
|
|
341
|
+
batchflags = [None, ] * len(newsections)
|
|
342
|
+
if batch:
|
|
343
|
+
if talkative:
|
|
344
|
+
t.sh.subtitle('Early-{:s} for all resources.'.format(doitmethod))
|
|
345
|
+
for ir, newsection in enumerate(newsections):
|
|
346
|
+
rhandler = newsection.rh
|
|
347
|
+
batchflags[ir] = getattr(newsection, 'early' + doitmethod)(**cmdopts)
|
|
348
|
+
if talkative:
|
|
349
|
+
if any(batchflags):
|
|
350
|
+
for ir, newsection in enumerate(newsections):
|
|
351
|
+
if talkative and batchflags[ir]:
|
|
352
|
+
logger.info('Resource no %02d/%02d: Early-%s registered with id: %s.',
|
|
353
|
+
ir + 1, len(rl), doitmethod, str(batchflags[ir]))
|
|
354
|
+
else:
|
|
355
|
+
logger.debug('Resource no %02d/%02d: Early-%s registered with id: %s.',
|
|
356
|
+
ir + 1, len(rl), doitmethod, str(batchflags[ir]))
|
|
357
|
+
else:
|
|
358
|
+
logger.info('Early-%s was unavailable for all of the resources.',
|
|
359
|
+
doitmethod)
|
|
360
|
+
# trigger finalise for all of the DelayedActions
|
|
361
|
+
tofinalise = [r_id for r_id in batchflags if r_id and r_id is not True]
|
|
362
|
+
if tofinalise:
|
|
363
|
+
if talkative:
|
|
364
|
+
t.sh.subtitle('Finalising all of the delayed actions...')
|
|
365
|
+
t.context.delayedactions_hub.finalise(* tofinalise)
|
|
366
|
+
secok = list()
|
|
367
|
+
for ir, newsection in enumerate(newsections):
|
|
368
|
+
rhandler = newsection.rh
|
|
369
|
+
# If quick get was ok for this resource don't call get again...
|
|
370
|
+
if talkative:
|
|
371
|
+
t.sh.subtitle('Resource no {:02d}/{:02d}'.format(ir + 1, len(rl)))
|
|
372
|
+
rhandler.quickview(nb=ir + 1, indent=0)
|
|
373
|
+
if batchflags[ir] is not True or (do_quick_insitu and quickget[ir]):
|
|
374
|
+
t.sh.highlight('Action {:s} on {:s}'.format(doitmethod.upper(),
|
|
375
|
+
rhandler.location(fatal=False)))
|
|
376
|
+
ok = do_quick_insitu and quickget[ir]
|
|
377
|
+
if batchflags[ir]:
|
|
378
|
+
actual_doitmethod = 'finalise' + doitmethod
|
|
379
|
+
ok = ok or getattr(newsection, actual_doitmethod)()
|
|
380
|
+
else:
|
|
381
|
+
actual_doitmethod = doitmethod
|
|
382
|
+
ok = ok or getattr(newsection, actual_doitmethod)(**cmdopts)
|
|
383
|
+
if talkative:
|
|
384
|
+
t.sh.highlight('Result from {:s}: [{!s}]'.format(actual_doitmethod, ok))
|
|
385
|
+
if talkative and not ok:
|
|
386
|
+
logger.error('Could not %s resource %s',
|
|
387
|
+
doitmethod, rhandler.container.localpath())
|
|
388
|
+
if not ok:
|
|
389
|
+
if complete:
|
|
390
|
+
logger.warning('Force complete for %s',
|
|
391
|
+
rhandler.location(fatal=False))
|
|
392
|
+
raise VortexForceComplete('Force task complete on resource error')
|
|
393
|
+
else:
|
|
394
|
+
secok.append(newsection)
|
|
395
|
+
if t.sh.trace:
|
|
396
|
+
print()
|
|
397
|
+
rlok.extend([newsection.rh for newsection in secok
|
|
398
|
+
if newsection.any_coherentgroup_opened])
|
|
399
|
+
else:
|
|
400
|
+
rlok.extend([newsection.rh for newsection in newsections])
|
|
401
|
+
|
|
402
|
+
return rlok
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
# noinspection PyShadowingBuiltins
|
|
406
|
+
def input(*args, **kw): # @ReservedAssignment
|
|
407
|
+
"""Add input :class:`~vortex.layout.dataflow.Section` objects to the current sequence.
|
|
408
|
+
|
|
409
|
+
Relies on the :func:`add_section` function (see its documentation), with:
|
|
410
|
+
|
|
411
|
+
* It's ``section`` attribute is automatically set to 'input';
|
|
412
|
+
* The ``kw``'s *insitu* item is set to :data:`active_insitu` by default.
|
|
413
|
+
|
|
414
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects (associated
|
|
415
|
+
with the newly created class:`~vortex.layout.dataflow.Section` objects).
|
|
416
|
+
"""
|
|
417
|
+
kw.setdefault('insitu', active_insitu)
|
|
418
|
+
kw.setdefault('batch', active_batchinputs)
|
|
419
|
+
return add_section('input', args, kw)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def inputs(ticket=None, context=None):
|
|
423
|
+
"""Return effective inputs for the specified context.
|
|
424
|
+
|
|
425
|
+
It actually returns both *inputs* and *executables*.
|
|
426
|
+
|
|
427
|
+
:param ~vortex.sessions.Ticket ticket: A session's Ticket. If set to *None*,
|
|
428
|
+
the current active session will be used (default: *None*)
|
|
429
|
+
:param ~vortex.layout.contexts.Context context: A context object. If set to *None*,
|
|
430
|
+
the current active context will be used (default: *None*)
|
|
431
|
+
:return: A list of :class:`~vortex.layout.dataflow.Section` objects.
|
|
432
|
+
"""
|
|
433
|
+
if context is None:
|
|
434
|
+
if ticket is None:
|
|
435
|
+
ticket = sessions.current()
|
|
436
|
+
context = ticket.context
|
|
437
|
+
return context.sequence.effective_inputs()
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def show_inputs(context=None):
|
|
441
|
+
"""Dump a summary of inputs (+ executables) sections.
|
|
442
|
+
|
|
443
|
+
:param ~vortex.layout.contexts.Context context: A context object. If set to *None*,
|
|
444
|
+
the current active context will be used (default: *None*)
|
|
445
|
+
"""
|
|
446
|
+
t = sessions.current()
|
|
447
|
+
for csi in inputs(ticket=t):
|
|
448
|
+
t.sh.header('Input ' + str(csi))
|
|
449
|
+
csi.show(ticket=t, context=context)
|
|
450
|
+
print()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def output(*args, **kw):
|
|
454
|
+
"""Add output :class:`~vortex.layout.dataflow.Section` objects to the current sequence.
|
|
455
|
+
|
|
456
|
+
Relies on the :func:`add_section` function (see its documentation), with:
|
|
457
|
+
|
|
458
|
+
* It's ``section`` attribute is automatically set to 'output';
|
|
459
|
+
|
|
460
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects (associated
|
|
461
|
+
with the newly created class:`~vortex.layout.dataflow.Section` objects).
|
|
462
|
+
"""
|
|
463
|
+
# Strip the metadatacheck option depending on active_metadatacheck
|
|
464
|
+
if not active_promise:
|
|
465
|
+
for target in ('promised', 'expected'):
|
|
466
|
+
if target in kw and kw[target]:
|
|
467
|
+
logger.info("The %s argument is removed since active_promise=False.", target)
|
|
468
|
+
del kw[target]
|
|
469
|
+
return add_section('output', args, kw)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def outputs(ticket=None, context=None):
|
|
473
|
+
"""Return effective outputs in specified context.
|
|
474
|
+
|
|
475
|
+
:param ~vortex.sessions.Ticket ticket: A session's Ticket. If set to *None*,
|
|
476
|
+
the current active session will be used (default: *None*)
|
|
477
|
+
:param ~vortex.layout.contexts.Context context: A context object. If set to *None*,
|
|
478
|
+
the current active context will be used (default: *None*)
|
|
479
|
+
:return: A list of :class:`~vortex.layout.dataflow.Section` objects.
|
|
480
|
+
"""
|
|
481
|
+
if context is None:
|
|
482
|
+
if ticket is None:
|
|
483
|
+
ticket = sessions.current()
|
|
484
|
+
context = ticket.context
|
|
485
|
+
return context.sequence.effective_outputs()
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def show_outputs(context=None):
|
|
489
|
+
"""Dump a summary of outputs sections.
|
|
490
|
+
|
|
491
|
+
:param ~vortex.layout.contexts.Context context: A context object. If set to *None*,
|
|
492
|
+
the current active context will be used (default: *None*)
|
|
493
|
+
"""
|
|
494
|
+
t = sessions.current()
|
|
495
|
+
for cso in outputs(ticket=t):
|
|
496
|
+
t.sh.header('Output ' + str(cso))
|
|
497
|
+
cso.show(ticket=t, context=context)
|
|
498
|
+
print()
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def promise(*args, **kw):
|
|
502
|
+
"""Log promises before execution.
|
|
503
|
+
|
|
504
|
+
Relies on the :func:`add_section` function (see its documentation), with:
|
|
505
|
+
|
|
506
|
+
* It's ``section`` attribute is automatically set to 'output';
|
|
507
|
+
* The ``kw``'s *promised* item is set to *True*;
|
|
508
|
+
* The ``kw``'s *force* item is set to *True*;
|
|
509
|
+
* The ``kw``'s *now* item is set to :data:`active_promise`.
|
|
510
|
+
|
|
511
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects (associated
|
|
512
|
+
with the newly created class:`~vortex.layout.dataflow.Section` objects).
|
|
513
|
+
"""
|
|
514
|
+
kw.update(
|
|
515
|
+
promised=True,
|
|
516
|
+
force=True,
|
|
517
|
+
now=active_promise,
|
|
518
|
+
)
|
|
519
|
+
if not active_promise:
|
|
520
|
+
kw.setdefault('verbose', False)
|
|
521
|
+
logger.warning('Promise flag is <%s> in that context', active_promise)
|
|
522
|
+
return add_section('output', args, kw)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def executable(*args, **kw):
|
|
526
|
+
"""Add executable :class:`~vortex.layout.dataflow.Section` objects to the current sequence.
|
|
527
|
+
|
|
528
|
+
Relies on the :func:`add_section` function (see its documentation), with:
|
|
529
|
+
|
|
530
|
+
* It's ``section`` attribute is automatically set to 'executable';
|
|
531
|
+
* The ``kw``'s *insitu* item is set to :data:`active_insitu` by default.
|
|
532
|
+
|
|
533
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects (associated
|
|
534
|
+
with the newly created class:`~vortex.layout.dataflow.Section` objects).
|
|
535
|
+
"""
|
|
536
|
+
kw.setdefault('insitu', active_insitu)
|
|
537
|
+
return add_section('executable', args, kw)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def algo(*args, **kw):
|
|
541
|
+
"""Load an algo component and display its description (if **verbose**).
|
|
542
|
+
|
|
543
|
+
1. The **kw** dictionary may contain keys that influence this function
|
|
544
|
+
behaviour (such attributes are popped from **kw** before going further):
|
|
545
|
+
|
|
546
|
+
* **loglevel**: The logging facility verbosity level that will be used
|
|
547
|
+
during the :class:`~vortex.layout.dataflow.Section` creation process.
|
|
548
|
+
If *None*, nothing is done (i.e. the current verbosity level is
|
|
549
|
+
preserved). (default: *None*).
|
|
550
|
+
* **verbose**: If *True*, print some informations on the standard output
|
|
551
|
+
(The default is given by :data:`active_verbose`).
|
|
552
|
+
|
|
553
|
+
2. The remaining **kw** items are passed directly to the "algo" footprint's
|
|
554
|
+
proxy in order to create the AlgoComponent object.
|
|
555
|
+
|
|
556
|
+
:return: an object that is a subtype of :class:`vortex.algo.components.AlgoComponent`
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
t = sessions.current()
|
|
560
|
+
|
|
561
|
+
# First, retrieve arguments of the toolbox command itself
|
|
562
|
+
loglevel = kw.pop('loglevel', None)
|
|
563
|
+
talkative = kw.pop('verbose', active_verbose)
|
|
564
|
+
|
|
565
|
+
with _tb_isolate(t, loglevel):
|
|
566
|
+
|
|
567
|
+
if talkative:
|
|
568
|
+
nicedump('Loading algo component with description:', **kw)
|
|
569
|
+
|
|
570
|
+
ok = proxy.component(**kw) # @UndefinedVariable
|
|
571
|
+
if ok and talkative:
|
|
572
|
+
print(t.line)
|
|
573
|
+
ok.quickview(nb=1, indent=0)
|
|
574
|
+
|
|
575
|
+
return ok
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def diff(*args, **kw):
|
|
579
|
+
"""Perform a diff with a resource with the same local name.
|
|
580
|
+
|
|
581
|
+
1. The **kw** dictionary may contain keys that influence this function
|
|
582
|
+
behaviour (such attributes are popped from **kw** before going further):
|
|
583
|
+
|
|
584
|
+
* **fatal**: If *True*, a :class:`ValueError` exception will be raised
|
|
585
|
+
whenever the "diff" detects differences.
|
|
586
|
+
* **loglevel**: The logging facility verbosity level that will be used
|
|
587
|
+
during the :class:`~vortex.layout.dataflow.Section` creation process.
|
|
588
|
+
If *None*, nothing is done (i.e. the current verbosity level is
|
|
589
|
+
preserved). (default: *None*).
|
|
590
|
+
* **verbose**: If *True*, print some informations on the standard output
|
|
591
|
+
(The default is given by :data:`active_verbose`).
|
|
592
|
+
|
|
593
|
+
2. The remaining **kw** items are passed directly to the :func:`rload`
|
|
594
|
+
function in order to create the resource's
|
|
595
|
+
:class:`~vortex.data.handlers.Handler` objects for the reference files.
|
|
596
|
+
|
|
597
|
+
3. The reference files resource's :class:`~vortex.data.handlers.Handler` objects
|
|
598
|
+
are altered so that the reference files are stored in temporary Containers.
|
|
599
|
+
|
|
600
|
+
4. The reference files are fetched.
|
|
601
|
+
|
|
602
|
+
5. The diff between the containers described in the resource's description
|
|
603
|
+
and the reference files is computed.
|
|
604
|
+
|
|
605
|
+
:return: A list of *diff* results.
|
|
606
|
+
"""
|
|
607
|
+
|
|
608
|
+
# First, retrieve arguments of the toolbox command itself
|
|
609
|
+
fatal = kw.pop('fatal', True)
|
|
610
|
+
loglevel = kw.pop('loglevel', None)
|
|
611
|
+
talkative = kw.pop('verbose', active_verbose)
|
|
612
|
+
batch = kw.pop('batch', active_batchinputs)
|
|
613
|
+
|
|
614
|
+
# Distinguish between section arguments, and resource loader arguments
|
|
615
|
+
opts, kwclean = stripargs_section(**kw)
|
|
616
|
+
|
|
617
|
+
# Show the actual set of arguments
|
|
618
|
+
if talkative:
|
|
619
|
+
nicedump('Discard section options', **opts)
|
|
620
|
+
nicedump('Resource handler description', **kwclean)
|
|
621
|
+
|
|
622
|
+
# Fast exit in case of undefined value
|
|
623
|
+
rlok = list()
|
|
624
|
+
none_skip = {k for k, v in kwclean.items()
|
|
625
|
+
if v is None and k in ('experiment', 'namespace')}
|
|
626
|
+
if none_skip:
|
|
627
|
+
logger.warning('Skip diff because of undefined argument(s)')
|
|
628
|
+
return rlok
|
|
629
|
+
|
|
630
|
+
t = sessions.current()
|
|
631
|
+
|
|
632
|
+
# Swich off autorecording of the current context + deal with loggging
|
|
633
|
+
with _tb_isolate(t, loglevel):
|
|
634
|
+
|
|
635
|
+
# Do not track the reference files
|
|
636
|
+
kwclean['storetrack'] = False
|
|
637
|
+
|
|
638
|
+
rhandlers = rload(*args, **kwclean)
|
|
639
|
+
sections = list()
|
|
640
|
+
earlyget_id = list()
|
|
641
|
+
source_container = list()
|
|
642
|
+
lazzy_container = list()
|
|
643
|
+
|
|
644
|
+
if batch:
|
|
645
|
+
# Early get
|
|
646
|
+
print(t.line)
|
|
647
|
+
for ir, rhandler in enumerate(rhandlers):
|
|
648
|
+
source_container.append(rhandler.container)
|
|
649
|
+
# Create a new container to hold the reference file
|
|
650
|
+
lazzy_container.append(footprints.proxy.container(shouldfly=True,
|
|
651
|
+
actualfmt=rhandler.container.actualfmt))
|
|
652
|
+
# Swapp the original container with the lazzy one
|
|
653
|
+
rhandler.container = lazzy_container[-1]
|
|
654
|
+
# Create a new section
|
|
655
|
+
sec = Section(rh=rhandler, kind=ixo.INPUT, intent=intent.IN, fatal=False)
|
|
656
|
+
sections.append(sec)
|
|
657
|
+
# Early-get
|
|
658
|
+
if rhandler.complete:
|
|
659
|
+
earlyget_id.append(sec.earlyget())
|
|
660
|
+
else:
|
|
661
|
+
earlyget_id.append(None)
|
|
662
|
+
# Finalising
|
|
663
|
+
if any([r_id and r_id is not True for r_id in earlyget_id]):
|
|
664
|
+
t.sh.highlight('Finalising Early-gets')
|
|
665
|
+
t.context.delayedactions_hub.finalise(* [r_id for r_id in earlyget_id
|
|
666
|
+
if r_id and r_id is not True])
|
|
667
|
+
|
|
668
|
+
for ir, rhandler in enumerate(rhandlers):
|
|
669
|
+
if talkative:
|
|
670
|
+
print(t.line)
|
|
671
|
+
rhandler.quickview(nb=ir + 1, indent=0)
|
|
672
|
+
print(t.line)
|
|
673
|
+
if not rhandler.complete:
|
|
674
|
+
logger.error('Incomplete Resource Handler for diff [%s]', rhandler)
|
|
675
|
+
if fatal:
|
|
676
|
+
raise ValueError('Incomplete Resource Handler for diff')
|
|
677
|
+
else:
|
|
678
|
+
rlok.append(False)
|
|
679
|
+
continue
|
|
680
|
+
|
|
681
|
+
# Get the reference file through a Section so that intent + fatal
|
|
682
|
+
# is properly dealt with... The section is discarded afterwards.
|
|
683
|
+
if batch:
|
|
684
|
+
rc = sections[ir].finaliseget()
|
|
685
|
+
else:
|
|
686
|
+
rc = sections[ir].get()
|
|
687
|
+
if not rc:
|
|
688
|
+
try:
|
|
689
|
+
logger.error('Cannot get the reference resource: %s',
|
|
690
|
+
rhandler.locate())
|
|
691
|
+
except Exception:
|
|
692
|
+
logger.error('Cannot get the reference resource: ???')
|
|
693
|
+
if fatal:
|
|
694
|
+
raise ValueError('Cannot get the reference resource')
|
|
695
|
+
else:
|
|
696
|
+
logger.info('The reference file is stored under: %s',
|
|
697
|
+
rhandler.container.localpath())
|
|
698
|
+
|
|
699
|
+
# What are the differences ?
|
|
700
|
+
if rc:
|
|
701
|
+
# priority is given to the diff implemented in the DataContent
|
|
702
|
+
if rhandler.resource.clscontents.is_diffable():
|
|
703
|
+
source_contents = rhandler.resource.contents_handler(
|
|
704
|
+
datafmt=source_container[ir].actualfmt)
|
|
705
|
+
source_contents.slurp(source_container[ir])
|
|
706
|
+
ref_contents = rhandler.contents
|
|
707
|
+
rc = source_contents.diff(ref_contents)
|
|
708
|
+
else:
|
|
709
|
+
rc = t.sh.diff(source_container[ir].localpath(),
|
|
710
|
+
rhandler.container.localpath(),
|
|
711
|
+
fmt=rhandler.container.actualfmt)
|
|
712
|
+
|
|
713
|
+
# Delete the reference file
|
|
714
|
+
lazzy_container[ir].clear()
|
|
715
|
+
|
|
716
|
+
# Now proceed with the result
|
|
717
|
+
logger.info('Diff return %s', str(rc))
|
|
718
|
+
t.context.diff_history.append_record(rc, source_container[ir], rhandler)
|
|
719
|
+
try:
|
|
720
|
+
logger.info('Diff result %s', str(rc.result))
|
|
721
|
+
except AttributeError:
|
|
722
|
+
pass
|
|
723
|
+
if not rc:
|
|
724
|
+
try:
|
|
725
|
+
logger.warning('Some diff occurred with %s', rhandler.locate())
|
|
726
|
+
except Exception:
|
|
727
|
+
logger.warning('Some diff occurred with ???')
|
|
728
|
+
try:
|
|
729
|
+
rc.result.differences()
|
|
730
|
+
except Exception:
|
|
731
|
+
pass
|
|
732
|
+
if fatal:
|
|
733
|
+
logger.critical('Difference in resource comparison is fatal')
|
|
734
|
+
raise ValueError('Fatal diff')
|
|
735
|
+
if t.sh.trace:
|
|
736
|
+
print()
|
|
737
|
+
rlok.append(rc)
|
|
738
|
+
|
|
739
|
+
return rlok
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def magic(localpath, **kw):
|
|
743
|
+
"""
|
|
744
|
+
Return a minimal resource handler build with an unknown resource,
|
|
745
|
+
a file container and an anonymous provider described with its URL.
|
|
746
|
+
"""
|
|
747
|
+
kw.update(
|
|
748
|
+
unknown=True,
|
|
749
|
+
magic='magic://localhost/' + localpath,
|
|
750
|
+
filename=localpath,
|
|
751
|
+
)
|
|
752
|
+
rhmagic = rh(**kw)
|
|
753
|
+
rhmagic.get()
|
|
754
|
+
return rhmagic
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def archive_refill(*args, **kw):
|
|
758
|
+
"""
|
|
759
|
+
Get a ressource in cache and upload it into the archive.
|
|
760
|
+
|
|
761
|
+
This will only have effect when working on a multistore.
|
|
762
|
+
|
|
763
|
+
The **kw** items are passed directly to the :func:`rload` function in
|
|
764
|
+
order to create the resource's :class:`~vortex.data.handlers.Handler`.
|
|
765
|
+
No "container" description is needed. One will be created by default.
|
|
766
|
+
|
|
767
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects.
|
|
768
|
+
"""
|
|
769
|
+
|
|
770
|
+
t = sessions.current()
|
|
771
|
+
|
|
772
|
+
# First, retrieve arguments of the toolbox command itself
|
|
773
|
+
loglevel = kw.pop('loglevel', None)
|
|
774
|
+
talkative = kw.pop('verbose', active_verbose)
|
|
775
|
+
|
|
776
|
+
with _tb_isolate(t, loglevel):
|
|
777
|
+
|
|
778
|
+
# Distinguish between section arguments, and resource loader arguments
|
|
779
|
+
opts, kwclean = stripargs_section(**kw)
|
|
780
|
+
fatal = opts.get('fatal', True)
|
|
781
|
+
|
|
782
|
+
# Print the user inputs
|
|
783
|
+
if talkative:
|
|
784
|
+
nicedump('Archive Refill Ressource+Provider description', **kwclean)
|
|
785
|
+
|
|
786
|
+
# Create the resource handlers
|
|
787
|
+
kwclean['container'] = footprints.proxy.container(uuid4fly=True,
|
|
788
|
+
uuid4flydir="archive_refills")
|
|
789
|
+
rl = rload(*args, **kwclean)
|
|
790
|
+
|
|
791
|
+
@contextmanager
|
|
792
|
+
def _fatal_wrap(action):
|
|
793
|
+
"""Handle errors during the calls to get or put."""
|
|
794
|
+
wrap_rc = dict(rc=True)
|
|
795
|
+
try:
|
|
796
|
+
yield wrap_rc
|
|
797
|
+
except Exception as e:
|
|
798
|
+
logger.error('Something wrong (action %s): %s. %s',
|
|
799
|
+
action, str(e), traceback.format_exc())
|
|
800
|
+
wrap_rc['rc'] = False
|
|
801
|
+
wrap_rc['exc'] = e
|
|
802
|
+
if fatal and not wrap_rc['rc']:
|
|
803
|
+
logger.critical('Fatal error with action %s.', action)
|
|
804
|
+
raise RuntimeError('Could not {:s} resource: {!s}'.format(action,
|
|
805
|
+
wrap_rc['rc']))
|
|
806
|
+
|
|
807
|
+
with t.sh.ftppool():
|
|
808
|
+
for ir, rhandler in enumerate(rl):
|
|
809
|
+
if talkative:
|
|
810
|
+
t.sh.subtitle('Resource no {:02d}/{:02d}'.format(ir + 1, len(rl)))
|
|
811
|
+
rhandler.quickview(nb=ir + 1, indent=0)
|
|
812
|
+
if not (rhandler.store.use_cache() and rhandler.store.use_archive()):
|
|
813
|
+
logger.info('The requested store does not have both the cache and archive capabilities. ' +
|
|
814
|
+
'Skipping this ressource handler.')
|
|
815
|
+
continue
|
|
816
|
+
with _fatal_wrap('get') as get_status:
|
|
817
|
+
get_status['rc'] = rhandler.get(incache=True, intent=intent.IN,
|
|
818
|
+
fmt=rhandler.resource.nativefmt)
|
|
819
|
+
put_status = dict(rc=False)
|
|
820
|
+
if get_status['rc']:
|
|
821
|
+
with _fatal_wrap('put') as put_status:
|
|
822
|
+
put_status['rc'] = rhandler.put(inarchive=True,
|
|
823
|
+
fmt=rhandler.resource.nativefmt)
|
|
824
|
+
rhandler.container.clear()
|
|
825
|
+
if talkative:
|
|
826
|
+
t.sh.highlight('Result from get: [{!s}], from put: [{!s}]'
|
|
827
|
+
.format(get_status['rc'], put_status['rc']))
|
|
828
|
+
|
|
829
|
+
return rl
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
def stack_archive_refill(*args, **kw):
|
|
833
|
+
"""Get a stack ressource in cache and upload it into the archive.
|
|
834
|
+
|
|
835
|
+
This will only have effect when working on a multistore.
|
|
836
|
+
|
|
837
|
+
The **kw** items are passed directly to the :func:`rload` function in
|
|
838
|
+
order to create the resource's :class:`~vortex.data.handlers.Handler`.
|
|
839
|
+
|
|
840
|
+
* No "container" description is needed. One will be created by default.
|
|
841
|
+
* The **block** attribute will be set automaticaly
|
|
842
|
+
|
|
843
|
+
:return: A list of :class:`vortex.data.handlers.Handler` objects.
|
|
844
|
+
"""
|
|
845
|
+
kw['block'] = 'stacks'
|
|
846
|
+
return archive_refill(*args, **kw)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def namespaces(**kw):
|
|
850
|
+
"""
|
|
851
|
+
Some kind of interactive help to find out quickly which namespaces are in
|
|
852
|
+
used. By default tracks ``stores`` and ``providers`` but one could give an
|
|
853
|
+
``only`` argument.
|
|
854
|
+
"""
|
|
855
|
+
rematch = re.compile('|'.join(kw.get('match', '.').split(',')),
|
|
856
|
+
re.IGNORECASE)
|
|
857
|
+
if 'only' in kw:
|
|
858
|
+
usedcat = kw['only'].split(',')
|
|
859
|
+
else:
|
|
860
|
+
usedcat = ('provider', 'store')
|
|
861
|
+
nameseen = dict()
|
|
862
|
+
for cat in [footprints.collectors.get(tag=x) for x in usedcat]:
|
|
863
|
+
for cls in cat():
|
|
864
|
+
fp = cls.footprint_retrieve().attr
|
|
865
|
+
netattr = fp.get('namespace', None)
|
|
866
|
+
if not netattr:
|
|
867
|
+
netattr = fp.get('netloc', None)
|
|
868
|
+
if netattr and 'values' in netattr:
|
|
869
|
+
for netname in filter(lambda x: rematch.search(x), netattr['values']):
|
|
870
|
+
if netname not in nameseen:
|
|
871
|
+
nameseen[netname] = list()
|
|
872
|
+
nameseen[netname].append(cls.fullname())
|
|
873
|
+
return nameseen
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def print_namespaces(**kw):
|
|
877
|
+
"""Formatted print of current namespaces."""
|
|
878
|
+
prefix = kw.pop('prefix', '+ ')
|
|
879
|
+
nd = namespaces(**kw)
|
|
880
|
+
justify = max([len(x) for x in nd.keys()])
|
|
881
|
+
linesep = ",\n" + ' ' * (justify + len(prefix) + 2)
|
|
882
|
+
for k, v in sorted(nd.items()):
|
|
883
|
+
nice_v = linesep.join(v) if len(v) > 1 else v[0]
|
|
884
|
+
print(prefix + k.ljust(justify), '[' + nice_v + ']')
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
def clear_promises(clear=None, netloc='promise.cache.fr', scheme='vortex',
|
|
888
|
+
storeoptions=None):
|
|
889
|
+
"""Remove all promises that have been made in the current session.
|
|
890
|
+
|
|
891
|
+
:param netloc: Netloc of the promise's cache store to clean up
|
|
892
|
+
:param scheme: Scheme of the promise's cache store to clean up
|
|
893
|
+
:param storeoptions: Option dictionary passed to the store (may be None)
|
|
894
|
+
"""
|
|
895
|
+
if clear is None:
|
|
896
|
+
clear = active_clear
|
|
897
|
+
if clear:
|
|
898
|
+
t = sessions.current()
|
|
899
|
+
myctx = t.context
|
|
900
|
+
for ctx in t.subcontexts:
|
|
901
|
+
ctx.activate()
|
|
902
|
+
ctx.clear_promises(netloc, scheme, storeoptions)
|
|
903
|
+
# Switch back to the previous context
|
|
904
|
+
myctx.activate()
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def rescue(*files, **opts):
|
|
908
|
+
"""Action to be undertaken when things really went bad."""
|
|
909
|
+
|
|
910
|
+
t = sessions.current()
|
|
911
|
+
sh = t.sh
|
|
912
|
+
env = t.env
|
|
913
|
+
|
|
914
|
+
# Summarise diffs...
|
|
915
|
+
if len(t.context.diff_history):
|
|
916
|
+
sh.header('Summary of automatic toolbox diffs')
|
|
917
|
+
t.context.diff_history.show()
|
|
918
|
+
|
|
919
|
+
# Force clearing of all promises
|
|
920
|
+
clear_promises(clear=True)
|
|
921
|
+
|
|
922
|
+
sh.header('Rescuing current dir')
|
|
923
|
+
sh.dir(output=False, fatal=False)
|
|
924
|
+
|
|
925
|
+
logger.info('Rescue files %s', files)
|
|
926
|
+
|
|
927
|
+
if 'VORTEX_RESCUE' in env and env.false('VORTEX_RESCUE'):
|
|
928
|
+
logger.warning('Skip rescue <VORTEX_RESCUE=%s>', env.VORTEX_RESCUE)
|
|
929
|
+
return False
|
|
930
|
+
|
|
931
|
+
if files:
|
|
932
|
+
items = list(files)
|
|
933
|
+
else:
|
|
934
|
+
items = sh.glob('*')
|
|
935
|
+
|
|
936
|
+
rfilter = opts.get('filter', env.VORTEX_RESCUE_FILTER)
|
|
937
|
+
if rfilter is not None:
|
|
938
|
+
logger.warning('Rescue filter <%s>', rfilter)
|
|
939
|
+
select = '|'.join(re.split(r'[,;:]+', rfilter))
|
|
940
|
+
items = [x for x in items if re.search(select, x, re.IGNORECASE)]
|
|
941
|
+
logger.info('Rescue filter [%s]', select)
|
|
942
|
+
|
|
943
|
+
rdiscard = opts.get('discard', env.VORTEX_RESCUE_DISCARD)
|
|
944
|
+
if rdiscard is not None:
|
|
945
|
+
logger.warning('Rescue discard <%s>', rdiscard)
|
|
946
|
+
select = '|'.join(re.split(r'[,;:]+', rdiscard))
|
|
947
|
+
items = [x for x in items if not re.search(select, x, re.IGNORECASE)]
|
|
948
|
+
logger.info('Rescue discard [%s]', select)
|
|
949
|
+
|
|
950
|
+
if items:
|
|
951
|
+
|
|
952
|
+
bkupdir = opts.get('bkupdir', env.VORTEX_RESCUE_PATH)
|
|
953
|
+
|
|
954
|
+
if bkupdir is None:
|
|
955
|
+
logger.error('No rescue directory defined.')
|
|
956
|
+
else:
|
|
957
|
+
logger.info('Backup directory defined by user < %s >', bkupdir)
|
|
958
|
+
items.sort()
|
|
959
|
+
logger.info('Rescue items %s', str(items))
|
|
960
|
+
sh.mkdir(bkupdir)
|
|
961
|
+
mkmove = False
|
|
962
|
+
st1 = sh.stat(sh.getcwd())
|
|
963
|
+
st2 = sh.stat(bkupdir)
|
|
964
|
+
if st1 and st2 and st1.st_dev == st2.st_dev:
|
|
965
|
+
mkmove = True
|
|
966
|
+
if mkmove:
|
|
967
|
+
thisrescue = sh.mv
|
|
968
|
+
else:
|
|
969
|
+
thisrescue = sh.cp
|
|
970
|
+
for ritem in items:
|
|
971
|
+
rtarget = sh.path.join(bkupdir, ritem)
|
|
972
|
+
if sh.path.exists(ritem) and not sh.path.islink(ritem):
|
|
973
|
+
if sh.path.isfile(ritem):
|
|
974
|
+
sh.rm(rtarget)
|
|
975
|
+
thisrescue(ritem, rtarget)
|
|
976
|
+
else:
|
|
977
|
+
thisrescue(ritem, rtarget)
|
|
978
|
+
|
|
979
|
+
else:
|
|
980
|
+
logger.warning('No item to rescue.')
|
|
981
|
+
|
|
982
|
+
return bool(items)
|