vortex-nwp 2.0.0b1__py3-none-any.whl → 2.0.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vortex/__init__.py +59 -45
- vortex/algo/__init__.py +3 -2
- vortex/algo/components.py +940 -614
- vortex/algo/mpitools.py +802 -497
- vortex/algo/serversynctools.py +34 -33
- vortex/config.py +19 -22
- vortex/data/__init__.py +9 -3
- vortex/data/abstractstores.py +593 -655
- vortex/data/containers.py +217 -162
- vortex/data/contents.py +65 -39
- vortex/data/executables.py +93 -102
- vortex/data/flow.py +40 -34
- vortex/data/geometries.py +228 -132
- vortex/data/handlers.py +428 -225
- vortex/data/outflow.py +15 -15
- vortex/data/providers.py +185 -163
- vortex/data/resources.py +48 -42
- vortex/data/stores.py +544 -413
- vortex/gloves.py +114 -87
- vortex/layout/__init__.py +1 -8
- vortex/layout/contexts.py +150 -84
- vortex/layout/dataflow.py +353 -202
- vortex/layout/monitor.py +264 -128
- vortex/nwp/__init__.py +5 -2
- vortex/nwp/algo/__init__.py +14 -5
- vortex/nwp/algo/assim.py +205 -151
- vortex/nwp/algo/clim.py +683 -517
- vortex/nwp/algo/coupling.py +447 -225
- vortex/nwp/algo/eda.py +437 -229
- vortex/nwp/algo/eps.py +403 -231
- vortex/nwp/algo/forecasts.py +420 -271
- vortex/nwp/algo/fpserver.py +683 -307
- vortex/nwp/algo/ifsnaming.py +205 -145
- vortex/nwp/algo/ifsroot.py +210 -122
- vortex/nwp/algo/monitoring.py +132 -76
- vortex/nwp/algo/mpitools.py +321 -191
- vortex/nwp/algo/odbtools.py +617 -353
- vortex/nwp/algo/oopsroot.py +449 -273
- vortex/nwp/algo/oopstests.py +90 -56
- vortex/nwp/algo/request.py +287 -206
- vortex/nwp/algo/stdpost.py +878 -522
- vortex/nwp/data/__init__.py +22 -4
- vortex/nwp/data/assim.py +125 -137
- vortex/nwp/data/boundaries.py +121 -68
- vortex/nwp/data/climfiles.py +193 -211
- vortex/nwp/data/configfiles.py +73 -69
- vortex/nwp/data/consts.py +426 -401
- vortex/nwp/data/ctpini.py +59 -43
- vortex/nwp/data/diagnostics.py +94 -66
- vortex/nwp/data/eda.py +50 -51
- vortex/nwp/data/eps.py +195 -146
- vortex/nwp/data/executables.py +440 -434
- vortex/nwp/data/fields.py +63 -48
- vortex/nwp/data/gridfiles.py +183 -111
- vortex/nwp/data/logs.py +250 -217
- vortex/nwp/data/modelstates.py +180 -151
- vortex/nwp/data/monitoring.py +72 -99
- vortex/nwp/data/namelists.py +254 -202
- vortex/nwp/data/obs.py +400 -308
- vortex/nwp/data/oopsexec.py +22 -20
- vortex/nwp/data/providers.py +90 -65
- vortex/nwp/data/query.py +71 -82
- vortex/nwp/data/stores.py +49 -36
- vortex/nwp/data/surfex.py +136 -137
- vortex/nwp/syntax/__init__.py +1 -1
- vortex/nwp/syntax/stdattrs.py +173 -111
- vortex/nwp/tools/__init__.py +2 -2
- vortex/nwp/tools/addons.py +22 -17
- vortex/nwp/tools/agt.py +24 -12
- vortex/nwp/tools/bdap.py +16 -5
- vortex/nwp/tools/bdcp.py +4 -1
- vortex/nwp/tools/bdm.py +3 -0
- vortex/nwp/tools/bdmp.py +14 -9
- vortex/nwp/tools/conftools.py +728 -378
- vortex/nwp/tools/drhook.py +12 -8
- vortex/nwp/tools/grib.py +65 -39
- vortex/nwp/tools/gribdiff.py +22 -17
- vortex/nwp/tools/ifstools.py +82 -42
- vortex/nwp/tools/igastuff.py +167 -143
- vortex/nwp/tools/mars.py +14 -2
- vortex/nwp/tools/odb.py +234 -125
- vortex/nwp/tools/partitioning.py +61 -37
- vortex/nwp/tools/satrad.py +27 -12
- vortex/nwp/util/async.py +83 -55
- vortex/nwp/util/beacon.py +10 -10
- vortex/nwp/util/diffpygram.py +174 -86
- vortex/nwp/util/ens.py +144 -63
- vortex/nwp/util/hooks.py +30 -19
- vortex/nwp/util/taskdeco.py +28 -24
- vortex/nwp/util/usepygram.py +278 -172
- vortex/nwp/util/usetnt.py +31 -17
- vortex/sessions.py +72 -39
- vortex/syntax/__init__.py +1 -1
- vortex/syntax/stdattrs.py +410 -171
- vortex/syntax/stddeco.py +31 -22
- vortex/toolbox.py +327 -192
- vortex/tools/__init__.py +11 -2
- vortex/tools/actions.py +125 -59
- vortex/tools/addons.py +111 -92
- vortex/tools/arm.py +42 -22
- vortex/tools/compression.py +72 -69
- vortex/tools/date.py +11 -4
- vortex/tools/delayedactions.py +242 -132
- vortex/tools/env.py +75 -47
- vortex/tools/folder.py +342 -171
- vortex/tools/grib.py +311 -149
- vortex/tools/lfi.py +423 -216
- vortex/tools/listings.py +109 -40
- vortex/tools/names.py +218 -156
- vortex/tools/net.py +632 -298
- vortex/tools/parallelism.py +93 -61
- vortex/tools/prestaging.py +55 -31
- vortex/tools/schedulers.py +172 -105
- vortex/tools/services.py +402 -333
- vortex/tools/storage.py +293 -358
- vortex/tools/surfex.py +24 -24
- vortex/tools/systems.py +1211 -631
- vortex/tools/targets.py +156 -100
- vortex/util/__init__.py +1 -1
- vortex/util/config.py +377 -327
- vortex/util/empty.py +2 -2
- vortex/util/helpers.py +56 -24
- vortex/util/introspection.py +18 -12
- vortex/util/iosponge.py +8 -4
- vortex/util/roles.py +4 -6
- vortex/util/storefunctions.py +39 -13
- vortex/util/structs.py +3 -3
- vortex/util/worker.py +29 -17
- vortex_nwp-2.0.0b2.dist-info/METADATA +66 -0
- vortex_nwp-2.0.0b2.dist-info/RECORD +142 -0
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/WHEEL +1 -1
- vortex/layout/appconf.py +0 -109
- vortex/layout/jobs.py +0 -1276
- vortex/layout/nodes.py +0 -1424
- vortex/layout/subjobs.py +0 -464
- vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
- vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/LICENSE +0 -0
- {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.0.0b2.dist-info}/top_level.txt +0 -0
vortex/util/empty.py
CHANGED
|
@@ -15,10 +15,10 @@ class DataConst:
|
|
|
15
15
|
|
|
16
16
|
def __init__(self, **kw):
|
|
17
17
|
self.__dict__.update(kw)
|
|
18
|
-
logger.debug(
|
|
18
|
+
logger.debug("DataConst init %s", self)
|
|
19
19
|
|
|
20
20
|
def __str__(self):
|
|
21
|
-
return super().__str__() +
|
|
21
|
+
return super().__str__() + " : " + str(sorted(self.__dict__.keys()))
|
|
22
22
|
|
|
23
23
|
def __contains__(self, item):
|
|
24
24
|
return item in self.__dict__
|
vortex/util/helpers.py
CHANGED
|
@@ -18,6 +18,7 @@ logger = loggers.getLogger(__name__)
|
|
|
18
18
|
|
|
19
19
|
class InputCheckerError(Exception):
|
|
20
20
|
"""Exception raised when the Input checking process fails."""
|
|
21
|
+
|
|
21
22
|
pass
|
|
22
23
|
|
|
23
24
|
|
|
@@ -38,15 +39,17 @@ def generic_input_checker(grouping_keys, min_items, *rhandlers, **kwargs):
|
|
|
38
39
|
"""
|
|
39
40
|
|
|
40
41
|
if len(rhandlers) == 0:
|
|
41
|
-
raise ValueError(
|
|
42
|
+
raise ValueError("At least one resource handler have to be provided")
|
|
42
43
|
# Just in case min_items is not an int...
|
|
43
44
|
min_items = int(min_items)
|
|
44
45
|
|
|
45
46
|
# Create a flat ResourceHandlers list (rhandlers may consists of lists)
|
|
46
47
|
flat_rhlist = []
|
|
47
48
|
flat_rhmandatory = []
|
|
48
|
-
for inlist, outlist in (
|
|
49
|
-
|
|
49
|
+
for inlist, outlist in (
|
|
50
|
+
(rhandlers, flat_rhlist),
|
|
51
|
+
(kwargs.pop("mandatory", []), flat_rhmandatory),
|
|
52
|
+
):
|
|
50
53
|
for rh in inlist:
|
|
51
54
|
if isinstance(rh, list) or isinstance(rh, tuple):
|
|
52
55
|
outlist.extend(rh)
|
|
@@ -63,46 +66,61 @@ def generic_input_checker(grouping_keys, min_items, *rhandlers, **kwargs):
|
|
|
63
66
|
rhgroups[tuple(keylist)].append(rh)
|
|
64
67
|
|
|
65
68
|
candidateslist = [
|
|
66
|
-
fp.stdtypes.FPDict(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
fp.stdtypes.FPDict(
|
|
70
|
+
{k: v for k, v in zip(grouping_keys, group) if v is not None}
|
|
71
|
+
)
|
|
69
72
|
for group in rhgroups.keys()
|
|
70
73
|
]
|
|
71
74
|
|
|
72
75
|
# Activate FTP connections pooling (for enhanced performances)
|
|
73
76
|
t = sessions.current()
|
|
74
77
|
with t.sh.ftppool():
|
|
75
|
-
|
|
76
78
|
# Check mandatory stuff
|
|
77
79
|
mychecks = [(rh, rh.check()) for rh in flat_rhmandatory]
|
|
78
80
|
if not all([acheck[1] for acheck in mychecks]):
|
|
79
81
|
for rh in [acheck[0] for acheck in mychecks if not acheck[1]]:
|
|
80
82
|
logger.error(" Missing location: %s", str(rh.locate()))
|
|
81
|
-
raise InputCheckerError(
|
|
83
|
+
raise InputCheckerError(
|
|
84
|
+
"Some of the mandatory resources are missing."
|
|
85
|
+
)
|
|
82
86
|
|
|
83
87
|
# Check call for non-mandatory stuff
|
|
84
88
|
outputlist = list()
|
|
85
89
|
# Is the check real or a delusion ?
|
|
86
|
-
fakecheck = kwargs.pop(
|
|
90
|
+
fakecheck = kwargs.pop("fakecheck", False)
|
|
87
91
|
# The keys are sorted so that results remains reproducible
|
|
88
92
|
for grouping_values in sorted(rhgroups.keys()):
|
|
89
|
-
mychecks = [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
mychecks = [
|
|
94
|
+
(rh, fakecheck or rh.check())
|
|
95
|
+
for rh in rhgroups[grouping_values]
|
|
96
|
+
]
|
|
97
|
+
groupid = fp.stdtypes.FPDict(
|
|
98
|
+
{
|
|
99
|
+
k: v
|
|
100
|
+
for k, v in zip(grouping_keys, grouping_values)
|
|
101
|
+
if v is not None
|
|
102
|
+
}
|
|
103
|
+
)
|
|
93
104
|
if all([acheck[1] for acheck in mychecks]):
|
|
94
105
|
outputlist.append(groupid)
|
|
95
|
-
logger.info(
|
|
106
|
+
logger.info(
|
|
107
|
+
"Group (%s): All the input files are accounted for.",
|
|
108
|
+
str(groupid),
|
|
109
|
+
)
|
|
96
110
|
else:
|
|
97
|
-
logger.warning(
|
|
98
|
-
|
|
111
|
+
logger.warning(
|
|
112
|
+
"Group (%s): Discarded because some of the input files are missing (see below).",
|
|
113
|
+
str(groupid),
|
|
114
|
+
)
|
|
99
115
|
for rh in [acheck[0] for acheck in mychecks if not acheck[1]]:
|
|
100
116
|
logger.warning(" Missing location: %s", str(rh.locate()))
|
|
101
117
|
|
|
102
118
|
# Enforce min_items
|
|
103
119
|
if len(outputlist) < min_items:
|
|
104
|
-
raise InputCheckerError(
|
|
105
|
-
|
|
120
|
+
raise InputCheckerError(
|
|
121
|
+
"The number of input groups is too small "
|
|
122
|
+
+ "({:d} < {:d})".format(len(outputlist), min_items)
|
|
123
|
+
)
|
|
106
124
|
|
|
107
125
|
return fp.stdtypes.FPList(outputlist), fp.stdtypes.FPList(candidateslist)
|
|
108
126
|
|
|
@@ -112,8 +130,12 @@ def members_input_checker(min_items, *rhandlers, **kwargs):
|
|
|
112
130
|
This is a shortcut for the generic_input_checher: only the member number is
|
|
113
131
|
considered and the return values corresponds to a list of members.
|
|
114
132
|
"""
|
|
115
|
-
mlist = [
|
|
116
|
-
|
|
133
|
+
mlist = [
|
|
134
|
+
desc["member"]
|
|
135
|
+
for desc in generic_input_checker(
|
|
136
|
+
("member",), min_items, *rhandlers, **kwargs
|
|
137
|
+
)[0]
|
|
138
|
+
]
|
|
117
139
|
return fp.stdtypes.FPList(sorted(mlist))
|
|
118
140
|
|
|
119
141
|
|
|
@@ -122,8 +144,12 @@ def colorfull_input_checker(min_items, *rhandlers, **kwargs):
|
|
|
122
144
|
This is a shortcut for the generic_input_checher: it returns a list of
|
|
123
145
|
dictionaries that described the available data.
|
|
124
146
|
"""
|
|
125
|
-
return generic_input_checker(
|
|
126
|
-
|
|
147
|
+
return generic_input_checker(
|
|
148
|
+
("vapp", "vconf", "experiment", "cutoff", "date", "member"),
|
|
149
|
+
min_items,
|
|
150
|
+
*rhandlers,
|
|
151
|
+
**kwargs,
|
|
152
|
+
)
|
|
127
153
|
|
|
128
154
|
|
|
129
155
|
def merge_contents(*kargs):
|
|
@@ -176,9 +202,15 @@ def mix_list(list_elements, date=None, member=None):
|
|
|
176
202
|
rgen.seed(seed)
|
|
177
203
|
else:
|
|
178
204
|
logger.info("The random seed not initialised")
|
|
179
|
-
logger.debug(
|
|
205
|
+
logger.debug(
|
|
206
|
+
"The list of elements is %s.",
|
|
207
|
+
" ".join([str(x) for x in list_elements]),
|
|
208
|
+
)
|
|
180
209
|
result_list_elements = list_elements
|
|
181
210
|
result_list_elements.sort()
|
|
182
211
|
rgen.shuffle(result_list_elements)
|
|
183
|
-
logger.debug(
|
|
212
|
+
logger.debug(
|
|
213
|
+
"The mixed list of elements is %s.",
|
|
214
|
+
" ".join([str(x) for x in result_list_elements]),
|
|
215
|
+
)
|
|
184
216
|
return result_list_elements
|
vortex/util/introspection.py
CHANGED
|
@@ -20,10 +20,10 @@ class Sherlock:
|
|
|
20
20
|
|
|
21
21
|
def __init__(self, **kw):
|
|
22
22
|
self.verbose = False
|
|
23
|
-
self.ticket = kw.pop(
|
|
23
|
+
self.ticket = kw.pop("ticket", sessions.current())
|
|
24
24
|
self.glove = self.ticket.glove
|
|
25
25
|
self.__dict__.update(kw)
|
|
26
|
-
logger.debug(
|
|
26
|
+
logger.debug("Sherlock init %s", self)
|
|
27
27
|
|
|
28
28
|
def rstfile(self, modpath):
|
|
29
29
|
"""Return the sphinx documentation associated to module reference or module path given."""
|
|
@@ -31,28 +31,34 @@ class Sherlock:
|
|
|
31
31
|
modpath = modpath.__file__
|
|
32
32
|
subpath = modpath
|
|
33
33
|
for installpath in self.glove.sitesrc:
|
|
34
|
-
subpath = re.sub(installpath,
|
|
35
|
-
subpath = re.sub(r
|
|
36
|
-
subpath = subpath.split(
|
|
37
|
-
if subpath[-1] ==
|
|
34
|
+
subpath = re.sub(installpath, "", subpath)
|
|
35
|
+
subpath = re.sub(r"\.pyc?", "", subpath)
|
|
36
|
+
subpath = subpath.split("/")
|
|
37
|
+
if subpath[-1] == "__init__":
|
|
38
38
|
subpath[-1] = subpath[-2]
|
|
39
|
-
subpath[-1] +=
|
|
39
|
+
subpath[-1] += ".rst"
|
|
40
40
|
|
|
41
|
-
subpath[1:1] = [
|
|
42
|
-
|
|
41
|
+
subpath[1:1] = [
|
|
42
|
+
"library",
|
|
43
|
+
]
|
|
44
|
+
return self.glove.sitedoc + "/".join(subpath)
|
|
43
45
|
|
|
44
46
|
def rstshort(self, filename):
|
|
45
47
|
"""Return relative path name of ``filename`` according to :meth:`siteroot`."""
|
|
46
|
-
return re.sub(self.glove.siteroot,
|
|
48
|
+
return re.sub(self.glove.siteroot, "", filename)[1:]
|
|
47
49
|
|
|
48
50
|
def getlocalmembers(self, obj, topmodule=None):
|
|
49
51
|
"""Return members of the module ``obj`` which are defined in the source file of the module."""
|
|
50
52
|
objs = dict()
|
|
51
53
|
if topmodule is None:
|
|
52
54
|
topmodule = obj
|
|
53
|
-
modfile = topmodule.__file__.rstrip(
|
|
55
|
+
modfile = topmodule.__file__.rstrip("c")
|
|
54
56
|
for x, y in inspect.getmembers(obj):
|
|
55
|
-
if
|
|
57
|
+
if (
|
|
58
|
+
inspect.isclass(y)
|
|
59
|
+
or inspect.isfunction(y)
|
|
60
|
+
or inspect.ismethod(y)
|
|
61
|
+
):
|
|
56
62
|
try:
|
|
57
63
|
if modfile == inspect.getsourcefile(y):
|
|
58
64
|
if self.verbose:
|
vortex/util/iosponge.py
CHANGED
|
@@ -23,7 +23,9 @@ class IoSponge(io.BufferedIOBase):
|
|
|
23
23
|
that limit the maximum of *size_check* and *guessed_size* is returned.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
def __init__(
|
|
26
|
+
def __init__(
|
|
27
|
+
self, rawio, size_check=IOSPONGE_DEFAULT_SIZECHECK, guessed_size=0
|
|
28
|
+
):
|
|
27
29
|
"""
|
|
28
30
|
:param file rawio: Any kind of file-like object
|
|
29
31
|
:param int size_check: The first size_check bytes will be buffered in
|
|
@@ -50,12 +52,14 @@ class IoSponge(io.BufferedIOBase):
|
|
|
50
52
|
return self._seek
|
|
51
53
|
|
|
52
54
|
def _generic_read(self, size, raw_read_cb):
|
|
53
|
-
ret = b
|
|
55
|
+
ret = b""
|
|
54
56
|
if self._seek < len(self._first_bytes):
|
|
55
57
|
if size is None:
|
|
56
|
-
ret = self._first_bytes[self._seek:]
|
|
58
|
+
ret = self._first_bytes[self._seek :]
|
|
57
59
|
else:
|
|
58
|
-
ret = self._first_bytes[
|
|
60
|
+
ret = self._first_bytes[
|
|
61
|
+
self._seek : min(self._size_check, self._seek + size)
|
|
62
|
+
]
|
|
59
63
|
if size is None:
|
|
60
64
|
ret += raw_read_cb(None)
|
|
61
65
|
elif len(ret) < size:
|
vortex/util/roles.py
CHANGED
|
@@ -5,15 +5,15 @@ Factory for named roles.
|
|
|
5
5
|
#: No automatic export
|
|
6
6
|
__all__ = []
|
|
7
7
|
|
|
8
|
-
_activetag =
|
|
8
|
+
_activetag = "default"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def stdfactoryrole(role):
|
|
12
12
|
"""Standard processing for role names."""
|
|
13
|
-
return
|
|
13
|
+
return "".join([s[0].upper() + s[1:] for s in role.split()])
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def switchfactory(tag=
|
|
16
|
+
def switchfactory(tag="default"):
|
|
17
17
|
"""Switch the current active factory to the existing one identified through its ``tag``."""
|
|
18
18
|
if tag in _rolesgateway:
|
|
19
19
|
global _activetag
|
|
@@ -46,6 +46,4 @@ def setrole(role, tag=None):
|
|
|
46
46
|
return _rolesgateway[tag](role)
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
_rolesgateway = dict(
|
|
50
|
-
default=stdfactoryrole
|
|
51
|
-
)
|
|
49
|
+
_rolesgateway = dict(default=stdfactoryrole)
|
vortex/util/storefunctions.py
CHANGED
|
@@ -31,28 +31,39 @@ def mergecontents(options):
|
|
|
31
31
|
|
|
32
32
|
:rtype: A file like object
|
|
33
33
|
"""
|
|
34
|
-
todo = options.get(
|
|
35
|
-
sort = vartrue.match(
|
|
34
|
+
todo = options.get("role", None)
|
|
35
|
+
sort = vartrue.match(
|
|
36
|
+
options.get(
|
|
37
|
+
"sort",
|
|
38
|
+
[
|
|
39
|
+
"false",
|
|
40
|
+
],
|
|
41
|
+
).pop()
|
|
42
|
+
)
|
|
36
43
|
if todo is not None:
|
|
37
44
|
ctx = sessions.current().context
|
|
38
45
|
sections = list()
|
|
39
46
|
for a_role in todo:
|
|
40
47
|
sections.extend(ctx.sequence.effective_inputs(role=a_role))
|
|
41
48
|
if len(sections) == 0:
|
|
42
|
-
raise FunctionStoreCallbackError(
|
|
49
|
+
raise FunctionStoreCallbackError(
|
|
50
|
+
"Nothing to store: the effective inputs sequence is void."
|
|
51
|
+
)
|
|
43
52
|
newcontent = helpers.merge_contents(sections)
|
|
44
53
|
if sort:
|
|
45
54
|
newcontent.sort()
|
|
46
55
|
else:
|
|
47
|
-
raise FunctionStoreCallbackError(
|
|
56
|
+
raise FunctionStoreCallbackError(
|
|
57
|
+
"At least one *role* option must be provided"
|
|
58
|
+
)
|
|
48
59
|
# Create a Virtual container and dump the new content inside it
|
|
49
60
|
virtualcont = fpx.container(incore=True)
|
|
50
61
|
newcontent.rewrite(virtualcont)
|
|
51
62
|
virtualcont.rewind()
|
|
52
63
|
# Force the new container to be in bytes mode
|
|
53
|
-
if virtualcont.actualmode and
|
|
54
|
-
virtualcont_b = fpx.container(incore=True, mode=
|
|
55
|
-
virtualcont_b.write(virtualcont.read().encode(encoding=
|
|
64
|
+
if virtualcont.actualmode and "b" not in virtualcont.actualmode:
|
|
65
|
+
virtualcont_b = fpx.container(incore=True, mode="w+b")
|
|
66
|
+
virtualcont_b.write(virtualcont.read().encode(encoding="utf-8"))
|
|
56
67
|
virtualcont = virtualcont_b
|
|
57
68
|
return virtualcont
|
|
58
69
|
|
|
@@ -68,12 +79,21 @@ def dumpinputs(options):
|
|
|
68
79
|
"""
|
|
69
80
|
t = sessions.current()
|
|
70
81
|
ctx = t.context
|
|
71
|
-
if vartrue.match(
|
|
82
|
+
if vartrue.match(
|
|
83
|
+
options.get(
|
|
84
|
+
"effective",
|
|
85
|
+
[
|
|
86
|
+
"true",
|
|
87
|
+
],
|
|
88
|
+
).pop()
|
|
89
|
+
):
|
|
72
90
|
sequence = ctx.sequence.effective_inputs()
|
|
73
91
|
else:
|
|
74
92
|
sequence = list(ctx.sequence.inputs())
|
|
75
93
|
if len(sequence) == 0:
|
|
76
|
-
raise FunctionStoreCallbackError(
|
|
94
|
+
raise FunctionStoreCallbackError(
|
|
95
|
+
"Nothing to store: the effective inputs sequence is void."
|
|
96
|
+
)
|
|
77
97
|
fileout = io.StringIO()
|
|
78
98
|
t.sh.json_dump([s.as_dict() for s in sequence], fileout, indent=4)
|
|
79
99
|
return fileout
|
|
@@ -87,17 +107,23 @@ def defaultinput(options):
|
|
|
87
107
|
content = dict()
|
|
88
108
|
|
|
89
109
|
def export_value(v):
|
|
90
|
-
if hasattr(v,
|
|
110
|
+
if hasattr(v, "footprint_export"):
|
|
91
111
|
return v.footprint_export()
|
|
92
|
-
elif hasattr(v,
|
|
112
|
+
elif hasattr(v, "export_dict"):
|
|
93
113
|
return v.export_dict()
|
|
94
114
|
else:
|
|
95
115
|
return v
|
|
96
116
|
|
|
97
117
|
for k, v in options.items():
|
|
98
118
|
if isinstance(k, str) and k.startswith(prefix):
|
|
99
|
-
content[k[len(prefix):]] = export_value(v)
|
|
119
|
+
content[k[len(prefix) :]] = export_value(v)
|
|
100
120
|
t = sessions.current()
|
|
101
121
|
fileout = io.StringIO()
|
|
102
|
-
t.sh.json_dump(
|
|
122
|
+
t.sh.json_dump(
|
|
123
|
+
[
|
|
124
|
+
content,
|
|
125
|
+
],
|
|
126
|
+
fileout,
|
|
127
|
+
indent=4,
|
|
128
|
+
)
|
|
103
129
|
return fileout
|
vortex/util/structs.py
CHANGED
|
@@ -17,10 +17,10 @@ class ShellEncoder(json.JSONEncoder):
|
|
|
17
17
|
|
|
18
18
|
def default(self, obj):
|
|
19
19
|
"""Overwrite the default encoding if the current object has a ``export_dict`` method."""
|
|
20
|
-
if hasattr(obj,
|
|
20
|
+
if hasattr(obj, "export_dict"):
|
|
21
21
|
return obj.export_dict()
|
|
22
|
-
elif hasattr(obj,
|
|
22
|
+
elif hasattr(obj, "footprint_export"):
|
|
23
23
|
return obj.footprint_export()
|
|
24
|
-
elif hasattr(obj,
|
|
24
|
+
elif hasattr(obj, "__dict__"):
|
|
25
25
|
return vars(obj)
|
|
26
26
|
return super().default(obj)
|
vortex/util/worker.py
CHANGED
|
@@ -47,12 +47,14 @@ class VortexWorker:
|
|
|
47
47
|
See :mod:`vortex.gloves`.
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
|
-
_PRIVATESESSION_TAG =
|
|
51
|
-
_PRIVATEGLOVE_TAG =
|
|
50
|
+
_PRIVATESESSION_TAG = "asyncworker_view"
|
|
51
|
+
_PRIVATEGLOVE_TAG = "asyncworker_id"
|
|
52
52
|
_PRIVATESESSION = None
|
|
53
53
|
_PRIVATEMODULES = set()
|
|
54
54
|
|
|
55
|
-
def __init__(
|
|
55
|
+
def __init__(
|
|
56
|
+
self, modules=tuple(), verbose=False, logger=None, profile=None
|
|
57
|
+
):
|
|
56
58
|
self._logger = logger
|
|
57
59
|
self._modules = modules
|
|
58
60
|
self._context_lock = False
|
|
@@ -74,21 +76,22 @@ class VortexWorker:
|
|
|
74
76
|
"""The session associated with Async Worker."""
|
|
75
77
|
if self._PRIVATESESSION is None:
|
|
76
78
|
import vortex
|
|
79
|
+
|
|
77
80
|
t = vortex.sessions.get(
|
|
78
81
|
tag=self._PRIVATESESSION_TAG,
|
|
79
82
|
glove=vortex.sessions.getglove(
|
|
80
|
-
tag=self._PRIVATEGLOVE_TAG,
|
|
81
|
-
|
|
82
|
-
)
|
|
83
|
+
tag=self._PRIVATEGLOVE_TAG, profile=self.profile
|
|
84
|
+
),
|
|
83
85
|
)
|
|
84
86
|
sh = t.system()
|
|
85
87
|
import vortex.tools.lfi # @UnusedImport
|
|
86
88
|
import vortex.tools.grib # @UnusedImport
|
|
87
89
|
import vortex.tools.folder # @UnusedImport
|
|
88
90
|
import footprints as fp
|
|
89
|
-
|
|
90
|
-
fp.proxy.addon(kind=
|
|
91
|
-
fp.proxy.addon(kind=
|
|
91
|
+
|
|
92
|
+
fp.proxy.addon(kind="lfi", shell=sh)
|
|
93
|
+
fp.proxy.addon(kind="grib", shell=sh)
|
|
94
|
+
fp.proxy.addon(kind="allfolders", shell=sh, verboseload=False)
|
|
92
95
|
self._PRIVATESESSION = t
|
|
93
96
|
return self._PRIVATESESSION
|
|
94
97
|
|
|
@@ -100,8 +103,8 @@ class VortexWorker:
|
|
|
100
103
|
if not self.verbose:
|
|
101
104
|
# footprints & bronx can be very talkative... we try to limit that !
|
|
102
105
|
global_level = logger.getEffectiveLevel()
|
|
103
|
-
f_logger = loggers.getLogger(
|
|
104
|
-
b_logger = loggers.getLogger(
|
|
106
|
+
f_logger = loggers.getLogger("footprints")
|
|
107
|
+
b_logger = loggers.getLogger("bronx")
|
|
105
108
|
if global_level <= logging.INFO and not self.verbose:
|
|
106
109
|
f_logger.setLevel(logging.INFO)
|
|
107
110
|
b_logger.setLevel(logging.INFO)
|
|
@@ -111,7 +114,9 @@ class VortexWorker:
|
|
|
111
114
|
|
|
112
115
|
def __enter__(self, *args):
|
|
113
116
|
if self._context_lock:
|
|
114
|
-
raise RuntimeError(
|
|
117
|
+
raise RuntimeError(
|
|
118
|
+
"Imbricated context manager calls are forbidden."
|
|
119
|
+
)
|
|
115
120
|
self._context_lock = True
|
|
116
121
|
if self.logger is None:
|
|
117
122
|
self._logger = logger
|
|
@@ -119,6 +124,7 @@ class VortexWorker:
|
|
|
119
124
|
self.reset_loggers(self.logger)
|
|
120
125
|
# Activate our own session
|
|
121
126
|
import vortex
|
|
127
|
+
|
|
122
128
|
self._context_prev_ticket = vortex.sessions.current()
|
|
123
129
|
if not self.session.active:
|
|
124
130
|
self.session.activate()
|
|
@@ -128,23 +134,29 @@ class VortexWorker:
|
|
|
128
134
|
self.session.sh.import_module(modname)
|
|
129
135
|
self._PRIVATEMODULES.add(modname)
|
|
130
136
|
# Ok, let's talk...
|
|
131
|
-
self.logger.info(
|
|
132
|
-
|
|
137
|
+
self.logger.info(
|
|
138
|
+
"VORTEX enter glove_profile=%s ", self.session.glove.profile
|
|
139
|
+
)
|
|
140
|
+
self.logger.debug(
|
|
141
|
+
" modules=%s addons=%s",
|
|
142
|
+
self.modules,
|
|
143
|
+
self.session.sh.loaded_addons(),
|
|
144
|
+
)
|
|
133
145
|
return self
|
|
134
146
|
|
|
135
147
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
136
148
|
"""Well... nothing much to do..."""
|
|
137
149
|
if exc_value is not None:
|
|
138
|
-
self.logger.critical(
|
|
150
|
+
self.logger.critical("VORTEX exits on error", exc_info=exc_value)
|
|
139
151
|
self.rc = False
|
|
140
152
|
else:
|
|
141
|
-
self.logger.debug(
|
|
153
|
+
self.logger.debug("VORTEX exits nicely.")
|
|
142
154
|
self._context_prev_ticket.activate()
|
|
143
155
|
self._context_lock = False
|
|
144
156
|
return True
|
|
145
157
|
|
|
146
158
|
|
|
147
|
-
if __name__ ==
|
|
159
|
+
if __name__ == "__main__":
|
|
148
160
|
import doctest
|
|
149
161
|
|
|
150
162
|
doctest.testmod(verbose=False)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: vortex-nwp
|
|
3
|
+
Version: 2.0.0b2
|
|
4
|
+
Summary: A Python library to write Numerical Weather Prediction pipelines components
|
|
5
|
+
Author-email: The Vortex Team <vortex.support@meteo.fr>
|
|
6
|
+
License: CECILL-C
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: License :: CeCILL-C Free Software License Agreement (CECILL-C)
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: bronx
|
|
13
|
+
Requires-Dist: footprints
|
|
14
|
+
Requires-Dist: taylorism
|
|
15
|
+
Requires-Dist: tomli
|
|
16
|
+
Requires-Dist: arpifs_listings
|
|
17
|
+
Provides-Extra: docs
|
|
18
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
19
|
+
Requires-Dist: sphinx-book-theme; extra == "docs"
|
|
20
|
+
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: ruff==0.9.1; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest; extra == "dev"
|
|
24
|
+
|
|
25
|
+
## vortex
|
|
26
|
+
|
|
27
|
+
A Python library to write individual tasks in Numerical Weather
|
|
28
|
+
Prediction pipelines.
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
Experiments in Numerical Weather Prediction (NWP) and related fields
|
|
33
|
+
consist in a series of computational tasks that can depend on each
|
|
34
|
+
other's output data. Each task is typically made of three successive
|
|
35
|
+
steps:
|
|
36
|
+
|
|
37
|
+
1. Fetch required input data.
|
|
38
|
+
2. Execute a program.
|
|
39
|
+
3. Make the program's output data available to subsequent tasks in the
|
|
40
|
+
pipeline.
|
|
41
|
+
|
|
42
|
+
Tasks have historically been written in some variant of the UNIX
|
|
43
|
+
shell, which was convenient to interact with the file system, manage
|
|
44
|
+
environment variables and execute programs. As NWP pipelines and
|
|
45
|
+
tasks grow more and more complex, however, there is a need for a
|
|
46
|
+
language providing more abstraction and code reuse mechanisms.
|
|
47
|
+
|
|
48
|
+
On top of the popular Python language, *vortex* provides abstractions
|
|
49
|
+
that encapsulate running -- potentially distributed -- programs as
|
|
50
|
+
well as fetching and storing the data they consume and generate.
|
|
51
|
+
|
|
52
|
+
### Documentation
|
|
53
|
+
|
|
54
|
+
The documentation is available at [vortex-nwp.readthedocs.io](https://vortex-nwp.readthedocs.io).
|
|
55
|
+
|
|
56
|
+
### Installation
|
|
57
|
+
|
|
58
|
+
Vortex can be installed using `pip` like most Python packages:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install vortex-nwp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Contributing
|
|
65
|
+
|
|
66
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|