vortex-nwp 2.0.0b1__py3-none-any.whl → 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. vortex/__init__.py +75 -47
  2. vortex/algo/__init__.py +3 -2
  3. vortex/algo/components.py +944 -618
  4. vortex/algo/mpitools.py +802 -497
  5. vortex/algo/mpitools_templates/__init__.py +1 -0
  6. vortex/algo/serversynctools.py +34 -33
  7. vortex/config.py +19 -22
  8. vortex/data/__init__.py +9 -3
  9. vortex/data/abstractstores.py +593 -655
  10. vortex/data/containers.py +217 -162
  11. vortex/data/contents.py +65 -39
  12. vortex/data/executables.py +93 -102
  13. vortex/data/flow.py +40 -34
  14. vortex/data/geometries.py +228 -132
  15. vortex/data/handlers.py +436 -227
  16. vortex/data/outflow.py +15 -15
  17. vortex/data/providers.py +185 -163
  18. vortex/data/resources.py +48 -42
  19. vortex/data/stores.py +540 -417
  20. vortex/data/sync_templates/__init__.py +0 -0
  21. vortex/gloves.py +114 -87
  22. vortex/layout/__init__.py +1 -8
  23. vortex/layout/contexts.py +150 -84
  24. vortex/layout/dataflow.py +353 -202
  25. vortex/layout/monitor.py +264 -128
  26. vortex/nwp/__init__.py +5 -2
  27. vortex/nwp/algo/__init__.py +14 -5
  28. vortex/nwp/algo/assim.py +205 -151
  29. vortex/nwp/algo/clim.py +683 -517
  30. vortex/nwp/algo/coupling.py +447 -225
  31. vortex/nwp/algo/eda.py +437 -229
  32. vortex/nwp/algo/eps.py +403 -231
  33. vortex/nwp/algo/forecasts.py +416 -275
  34. vortex/nwp/algo/fpserver.py +683 -307
  35. vortex/nwp/algo/ifsnaming.py +205 -145
  36. vortex/nwp/algo/ifsroot.py +215 -122
  37. vortex/nwp/algo/monitoring.py +137 -76
  38. vortex/nwp/algo/mpitools.py +330 -190
  39. vortex/nwp/algo/odbtools.py +637 -353
  40. vortex/nwp/algo/oopsroot.py +454 -273
  41. vortex/nwp/algo/oopstests.py +90 -56
  42. vortex/nwp/algo/request.py +287 -206
  43. vortex/nwp/algo/stdpost.py +878 -522
  44. vortex/nwp/data/__init__.py +22 -4
  45. vortex/nwp/data/assim.py +125 -137
  46. vortex/nwp/data/boundaries.py +121 -68
  47. vortex/nwp/data/climfiles.py +193 -211
  48. vortex/nwp/data/configfiles.py +73 -69
  49. vortex/nwp/data/consts.py +426 -401
  50. vortex/nwp/data/ctpini.py +59 -43
  51. vortex/nwp/data/diagnostics.py +94 -66
  52. vortex/nwp/data/eda.py +50 -51
  53. vortex/nwp/data/eps.py +195 -146
  54. vortex/nwp/data/executables.py +440 -434
  55. vortex/nwp/data/fields.py +63 -48
  56. vortex/nwp/data/gridfiles.py +183 -111
  57. vortex/nwp/data/logs.py +250 -217
  58. vortex/nwp/data/modelstates.py +180 -151
  59. vortex/nwp/data/monitoring.py +72 -99
  60. vortex/nwp/data/namelists.py +254 -202
  61. vortex/nwp/data/obs.py +400 -308
  62. vortex/nwp/data/oopsexec.py +22 -20
  63. vortex/nwp/data/providers.py +90 -65
  64. vortex/nwp/data/query.py +71 -82
  65. vortex/nwp/data/stores.py +49 -36
  66. vortex/nwp/data/surfex.py +136 -137
  67. vortex/nwp/syntax/__init__.py +1 -1
  68. vortex/nwp/syntax/stdattrs.py +173 -111
  69. vortex/nwp/tools/__init__.py +2 -2
  70. vortex/nwp/tools/addons.py +22 -17
  71. vortex/nwp/tools/agt.py +24 -12
  72. vortex/nwp/tools/bdap.py +16 -5
  73. vortex/nwp/tools/bdcp.py +4 -1
  74. vortex/nwp/tools/bdm.py +3 -0
  75. vortex/nwp/tools/bdmp.py +14 -9
  76. vortex/nwp/tools/conftools.py +728 -378
  77. vortex/nwp/tools/drhook.py +12 -8
  78. vortex/nwp/tools/grib.py +65 -39
  79. vortex/nwp/tools/gribdiff.py +22 -17
  80. vortex/nwp/tools/ifstools.py +82 -42
  81. vortex/nwp/tools/igastuff.py +167 -143
  82. vortex/nwp/tools/mars.py +14 -2
  83. vortex/nwp/tools/odb.py +234 -125
  84. vortex/nwp/tools/partitioning.py +61 -37
  85. vortex/nwp/tools/satrad.py +27 -12
  86. vortex/nwp/util/async.py +83 -55
  87. vortex/nwp/util/beacon.py +10 -10
  88. vortex/nwp/util/diffpygram.py +174 -86
  89. vortex/nwp/util/ens.py +144 -63
  90. vortex/nwp/util/hooks.py +30 -19
  91. vortex/nwp/util/taskdeco.py +28 -24
  92. vortex/nwp/util/usepygram.py +278 -172
  93. vortex/nwp/util/usetnt.py +31 -17
  94. vortex/sessions.py +72 -39
  95. vortex/syntax/__init__.py +1 -1
  96. vortex/syntax/stdattrs.py +410 -171
  97. vortex/syntax/stddeco.py +31 -22
  98. vortex/toolbox.py +327 -192
  99. vortex/tools/__init__.py +11 -2
  100. vortex/tools/actions.py +110 -121
  101. vortex/tools/addons.py +111 -92
  102. vortex/tools/arm.py +42 -22
  103. vortex/tools/compression.py +72 -69
  104. vortex/tools/date.py +11 -4
  105. vortex/tools/delayedactions.py +242 -132
  106. vortex/tools/env.py +75 -47
  107. vortex/tools/folder.py +342 -171
  108. vortex/tools/grib.py +341 -162
  109. vortex/tools/lfi.py +423 -216
  110. vortex/tools/listings.py +109 -40
  111. vortex/tools/names.py +218 -156
  112. vortex/tools/net.py +655 -299
  113. vortex/tools/parallelism.py +93 -61
  114. vortex/tools/prestaging.py +55 -31
  115. vortex/tools/schedulers.py +172 -105
  116. vortex/tools/services.py +403 -334
  117. vortex/tools/storage.py +293 -358
  118. vortex/tools/surfex.py +24 -24
  119. vortex/tools/systems.py +1234 -643
  120. vortex/tools/targets.py +156 -100
  121. vortex/util/__init__.py +1 -1
  122. vortex/util/config.py +378 -327
  123. vortex/util/empty.py +2 -2
  124. vortex/util/helpers.py +56 -24
  125. vortex/util/introspection.py +18 -12
  126. vortex/util/iosponge.py +8 -4
  127. vortex/util/roles.py +4 -6
  128. vortex/util/storefunctions.py +39 -13
  129. vortex/util/structs.py +3 -3
  130. vortex/util/worker.py +29 -17
  131. vortex_nwp-2.1.0.dist-info/METADATA +67 -0
  132. vortex_nwp-2.1.0.dist-info/RECORD +144 -0
  133. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info}/WHEEL +1 -1
  134. vortex/layout/appconf.py +0 -109
  135. vortex/layout/jobs.py +0 -1276
  136. vortex/layout/nodes.py +0 -1424
  137. vortex/layout/subjobs.py +0 -464
  138. vortex_nwp-2.0.0b1.dist-info/METADATA +0 -50
  139. vortex_nwp-2.0.0b1.dist-info/RECORD +0 -146
  140. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.dist-info/licenses}/LICENSE +0 -0
  141. {vortex_nwp-2.0.0b1.dist-info → vortex_nwp-2.1.0.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('DataConst init %s', self)
18
+ logger.debug("DataConst init %s", self)
19
19
 
20
20
  def __str__(self):
21
- return super().__str__() + ' : ' + str(sorted(self.__dict__.keys()))
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('At least one resource handler have to be provided')
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 ((rhandlers, flat_rhlist),
49
- (kwargs.pop('mandatory', []), flat_rhmandatory),):
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({k: v
67
- for k, v in zip(grouping_keys, group)
68
- if v is not None})
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("Some of the mandatory resources are missing.")
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('fakecheck', False)
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 = [(rh, fakecheck or rh.check()) for rh in rhgroups[grouping_values]]
90
- groupid = fp.stdtypes.FPDict({k: v
91
- for k, v in zip(grouping_keys, grouping_values)
92
- if v is not None})
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("Group (%s): All the input files are accounted for.", str(groupid))
106
+ logger.info(
107
+ "Group (%s): All the input files are accounted for.",
108
+ str(groupid),
109
+ )
96
110
  else:
97
- logger.warning("Group (%s): Discarded because some of the input files are missing (see below).",
98
- str(groupid))
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("The number of input groups is too small " +
105
- "({:d} < {:d})".format(len(outputlist), min_items))
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 = [desc['member'] for desc in generic_input_checker(('member', ), min_items,
116
- *rhandlers, **kwargs)[0]]
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(('vapp', 'vconf', 'experiment', 'cutoff', 'date', 'member'),
126
- min_items, *rhandlers, **kwargs)
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("The list of elements is %s.", " ".join([str(x) for x in list_elements]))
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("The mixed list of elements is %s.", " ".join([str(x) for x in result_list_elements]))
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
@@ -20,10 +20,10 @@ class Sherlock:
20
20
 
21
21
  def __init__(self, **kw):
22
22
  self.verbose = False
23
- self.ticket = kw.pop('ticket', sessions.current())
23
+ self.ticket = kw.pop("ticket", sessions.current())
24
24
  self.glove = self.ticket.glove
25
25
  self.__dict__.update(kw)
26
- logger.debug('Sherlock init %s', self)
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, '', subpath)
35
- subpath = re.sub(r'\.pyc?', '', subpath)
36
- subpath = subpath.split('/')
37
- if subpath[-1] == '__init__':
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] += '.rst'
39
+ subpath[-1] += ".rst"
40
40
 
41
- subpath[1:1] = ['library', ]
42
- return self.glove.sitedoc + '/'.join(subpath)
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, '', filename)[1:]
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('c')
55
+ modfile = topmodule.__file__.rstrip("c")
54
56
  for x, y in inspect.getmembers(obj):
55
- if inspect.isclass(y) or inspect.isfunction(y) or inspect.ismethod(y):
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__(self, rawio, size_check=IOSPONGE_DEFAULT_SIZECHECK, guessed_size=0):
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[self._seek:min(self._size_check, self._seek + size)]
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 = 'default'
8
+ _activetag = "default"
9
9
 
10
10
 
11
11
  def stdfactoryrole(role):
12
12
  """Standard processing for role names."""
13
- return ''.join([s[0].upper() + s[1:] for s in role.split()])
13
+ return "".join([s[0].upper() + s[1:] for s in role.split()])
14
14
 
15
15
 
16
- def switchfactory(tag='default'):
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)
@@ -31,28 +31,39 @@ def mergecontents(options):
31
31
 
32
32
  :rtype: A file like object
33
33
  """
34
- todo = options.get('role', None)
35
- sort = vartrue.match(options.get('sort', ['false', ]).pop())
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("Nothing to store: the effective inputs sequence is void.")
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('At least one *role* option must be provided')
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 'b' not in virtualcont.actualmode:
54
- virtualcont_b = fpx.container(incore=True, mode='w+b')
55
- virtualcont_b.write(virtualcont.read().encode(encoding='utf-8'))
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(options.get('effective', ['true', ]).pop()):
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("Nothing to store: the effective inputs sequence is void.")
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, 'footprint_export'):
110
+ if hasattr(v, "footprint_export"):
91
111
  return v.footprint_export()
92
- elif hasattr(v, 'export_dict'):
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([content, ], fileout, indent=4)
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, 'export_dict'):
20
+ if hasattr(obj, "export_dict"):
21
21
  return obj.export_dict()
22
- elif hasattr(obj, 'footprint_export'):
22
+ elif hasattr(obj, "footprint_export"):
23
23
  return obj.footprint_export()
24
- elif hasattr(obj, '__dict__'):
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 = 'asyncworker_view'
51
- _PRIVATEGLOVE_TAG = 'asyncworker_id'
50
+ _PRIVATESESSION_TAG = "asyncworker_view"
51
+ _PRIVATEGLOVE_TAG = "asyncworker_id"
52
52
  _PRIVATESESSION = None
53
53
  _PRIVATEMODULES = set()
54
54
 
55
- def __init__(self, modules=tuple(), verbose=False, logger=None, profile=None):
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
- profile=self.profile
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
- fp.proxy.addon(kind='lfi', shell=sh)
90
- fp.proxy.addon(kind='grib', shell=sh)
91
- fp.proxy.addon(kind='allfolders', shell=sh, verboseload=False)
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('footprints')
104
- b_logger = loggers.getLogger('bronx')
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('Imbricated context manager calls are forbidden.')
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('VORTEX enter glove_profile=%s ', self.session.glove.profile)
132
- self.logger.debug(' modules=%s addons=%s', self.modules, self.session.sh.loaded_addons())
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('VORTEX exits on error', exc_info=exc_value)
150
+ self.logger.critical("VORTEX exits on error", exc_info=exc_value)
139
151
  self.rc = False
140
152
  else:
141
- self.logger.debug('VORTEX exits nicely.')
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__ == '__main__':
159
+ if __name__ == "__main__":
148
160
  import doctest
149
161
 
150
162
  doctest.testmod(verbose=False)
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: vortex-nwp
3
+ Version: 2.1.0
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
+ Dynamic: license-file
25
+
26
+ ## vortex
27
+
28
+ A Python library to write individual tasks in Numerical Weather
29
+ Prediction pipelines.
30
+
31
+ ![A blue coloured vortex pointing downwards on a lighter blue background](vortex.png)
32
+
33
+ Experiments in Numerical Weather Prediction (NWP) and related fields
34
+ consist in a series of computational tasks that can depend on each
35
+ other's output data. Each task is typically made of three successive
36
+ steps:
37
+
38
+ 1. Fetch required input data.
39
+ 2. Execute a program.
40
+ 3. Make the program's output data available to subsequent tasks in the
41
+ pipeline.
42
+
43
+ Tasks have historically been written in some variant of the UNIX
44
+ shell, which was convenient to interact with the file system, manage
45
+ environment variables and execute programs. As NWP pipelines and
46
+ tasks grow more and more complex, however, there is a need for a
47
+ language providing more abstraction and code reuse mechanisms.
48
+
49
+ On top of the popular Python language, *vortex* provides abstractions
50
+ that encapsulate running -- potentially distributed -- programs as
51
+ well as fetching and storing the data they consume and generate.
52
+
53
+ ### Documentation
54
+
55
+ The documentation is available at [vortex-nwp.readthedocs.io](https://vortex-nwp.readthedocs.io).
56
+
57
+ ### Installation
58
+
59
+ Vortex can be installed using `pip` like most Python packages:
60
+
61
+ ```bash
62
+ pip install vortex-nwp
63
+ ```
64
+
65
+ ### Contributing
66
+
67
+ See [CONTRIBUTING.md](CONTRIBUTING.md).