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/gloves.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GLObal Versatile Environment classes are responsible for session-wide
|
|
3
|
+
configuration (username, emil adress, ...)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from bronx.fancies import loggers
|
|
7
|
+
import footprints
|
|
8
|
+
|
|
9
|
+
from vortex.tools.env import Environment
|
|
10
|
+
|
|
11
|
+
#: No automatic export
|
|
12
|
+
__all__ = []
|
|
13
|
+
|
|
14
|
+
logger = loggers.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Glove(footprints.FootprintBase):
|
|
18
|
+
"""Base class for GLObal Versatile Environment."""
|
|
19
|
+
|
|
20
|
+
_abstract = True
|
|
21
|
+
_collector = ('glove',)
|
|
22
|
+
_footprint = dict(
|
|
23
|
+
info = 'Abstract glove',
|
|
24
|
+
attr = dict(
|
|
25
|
+
email = dict(
|
|
26
|
+
alias = ['address'],
|
|
27
|
+
optional = True,
|
|
28
|
+
default = Environment(active=False)['email'],
|
|
29
|
+
access = 'rwx',
|
|
30
|
+
),
|
|
31
|
+
vapp = dict(
|
|
32
|
+
optional = True,
|
|
33
|
+
default = 'play',
|
|
34
|
+
access = 'rwx',
|
|
35
|
+
),
|
|
36
|
+
vconf = dict(
|
|
37
|
+
optional = True,
|
|
38
|
+
default = 'sandbox',
|
|
39
|
+
access = 'rwx',
|
|
40
|
+
),
|
|
41
|
+
tag = dict(
|
|
42
|
+
optional = True,
|
|
43
|
+
default = 'default',
|
|
44
|
+
),
|
|
45
|
+
user = dict(
|
|
46
|
+
alias = ('logname', 'username'),
|
|
47
|
+
optional = True,
|
|
48
|
+
default = Environment(active=False)['logname']
|
|
49
|
+
),
|
|
50
|
+
profile = dict(
|
|
51
|
+
alias = ('kind', 'membership'),
|
|
52
|
+
values = ['oper', 'dble', 'test', 'research', 'tourist'],
|
|
53
|
+
remap = dict(tourist = 'research')
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def __init__(self, *args, **kw):
|
|
59
|
+
logger.debug('Glove abstract %s init', self.__class__)
|
|
60
|
+
super().__init__(*args, **kw)
|
|
61
|
+
self._rmdepthmin = 3
|
|
62
|
+
self._siteroot = None
|
|
63
|
+
self._siteconf = None
|
|
64
|
+
self._sitedoc = None
|
|
65
|
+
self._sitesrc = None
|
|
66
|
+
self._ftdhost = None
|
|
67
|
+
self._ftduser = None
|
|
68
|
+
self._ftusers = dict()
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def realkind(self):
|
|
72
|
+
"""Returns the litteral string identity of the current glove."""
|
|
73
|
+
return 'glove'
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def configrc(self):
|
|
77
|
+
"""Returns the path of the default directory where ``.ini`` files are stored."""
|
|
78
|
+
return Environment(active=False).HOME + '/.vortexrc'
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def siteroot(self):
|
|
82
|
+
"""Returns the path of the vortex install directory."""
|
|
83
|
+
if not self._siteroot:
|
|
84
|
+
self._siteroot = '/'.join(__file__.split('/')[0:-3])
|
|
85
|
+
return self._siteroot
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def siteconf(self):
|
|
89
|
+
"""Returns the path of the default directory where ``.ini`` files are stored."""
|
|
90
|
+
if not self._siteconf:
|
|
91
|
+
self._siteconf = '/'.join((self.siteroot, 'conf'))
|
|
92
|
+
return self._siteconf
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def sitedoc(self):
|
|
96
|
+
"""Returns the path of the default directory where ``.ini`` files are stored."""
|
|
97
|
+
if not self._sitedoc:
|
|
98
|
+
self._sitedoc = '/'.join((self.siteroot, 'sphinx'))
|
|
99
|
+
return self._sitedoc
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def sitesrc(self):
|
|
103
|
+
"""Returns the path of the default directory where ``.ini`` files are stored."""
|
|
104
|
+
if not self._sitesrc:
|
|
105
|
+
self._sitesrc = (
|
|
106
|
+
'/'.join((self.siteroot, 'site')),
|
|
107
|
+
'/'.join((self.siteroot, 'src'))
|
|
108
|
+
)
|
|
109
|
+
return self._sitesrc
|
|
110
|
+
|
|
111
|
+
def setenv(self, app=None, conf=None):
|
|
112
|
+
"""Change ``vapp`` or/and ``vconf`` in one call."""
|
|
113
|
+
if app is not None:
|
|
114
|
+
self.vapp = app
|
|
115
|
+
if conf is not None:
|
|
116
|
+
self.vconf = conf
|
|
117
|
+
return (self.vapp, self.vconf)
|
|
118
|
+
|
|
119
|
+
def setmail(self, domain=None):
|
|
120
|
+
"""Refresh actual email with current username and provided ``domain``."""
|
|
121
|
+
if domain is None:
|
|
122
|
+
from vortex import sessions
|
|
123
|
+
domain = sessions.system().getfqdn()
|
|
124
|
+
return '@'.join((self.user, domain))
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def xmail(self):
|
|
128
|
+
if self.email is None:
|
|
129
|
+
return self.setmail()
|
|
130
|
+
else:
|
|
131
|
+
return self.email
|
|
132
|
+
|
|
133
|
+
def safedirs(self):
|
|
134
|
+
"""Protected paths as a list a tuples (path, depth)."""
|
|
135
|
+
e = Environment(active=False)
|
|
136
|
+
return [(e.HOME, 2), (e.TMPDIR, 1)]
|
|
137
|
+
|
|
138
|
+
def setftuser(self, user, hostname=None):
|
|
139
|
+
"""Register a default username for *hostname*.
|
|
140
|
+
|
|
141
|
+
If *hostname* is omitted the default username is set.
|
|
142
|
+
"""
|
|
143
|
+
if hostname is None:
|
|
144
|
+
self._ftduser = user
|
|
145
|
+
else:
|
|
146
|
+
if not user:
|
|
147
|
+
del self._ftusers[hostname]
|
|
148
|
+
else:
|
|
149
|
+
self._ftusers[hostname] = user
|
|
150
|
+
|
|
151
|
+
def getftuser(self, hostname, defaults_to_user=True):
|
|
152
|
+
"""Get the default username for a given *hostname*."""
|
|
153
|
+
if self._ftusers.get(hostname, None):
|
|
154
|
+
return self._ftusers[hostname]
|
|
155
|
+
else:
|
|
156
|
+
if self._ftduser:
|
|
157
|
+
return self._ftduser
|
|
158
|
+
else:
|
|
159
|
+
return Environment.current().get('VORTEX_ARCHIVE_USER',
|
|
160
|
+
self.user if defaults_to_user else None)
|
|
161
|
+
|
|
162
|
+
def _get_default_fthost(self):
|
|
163
|
+
if self._ftdhost:
|
|
164
|
+
return self._ftdhost
|
|
165
|
+
else:
|
|
166
|
+
return Environment.current().get('VORTEX_ARCHIVE_HOST', None)
|
|
167
|
+
|
|
168
|
+
def _set_default_fthost(self, value):
|
|
169
|
+
self._ftdhost = value
|
|
170
|
+
|
|
171
|
+
def _del_default_fthost(self):
|
|
172
|
+
self._ftdhost = None
|
|
173
|
+
|
|
174
|
+
default_fthost = property(_get_default_fthost, _set_default_fthost, _del_default_fthost)
|
|
175
|
+
|
|
176
|
+
def describeftsettings(self, indent='+ '):
|
|
177
|
+
"""Returns a printable description of default file transfert usernames."""
|
|
178
|
+
card = "\n".join(
|
|
179
|
+
['{0}{3:48s} = {4:s}', ] +
|
|
180
|
+
['{0}{1:48s} = {2:s}', ] +
|
|
181
|
+
(['{0}Host specific FT users:', ] if self._ftusers else []) +
|
|
182
|
+
['{0}' + ' {:46s} = {:s}'.format(k, v) for k, v in self._ftusers.items() if v]
|
|
183
|
+
).format(indent,
|
|
184
|
+
'Default FT User', str(self._ftduser),
|
|
185
|
+
'Default FT Host', str(self._ftdhost))
|
|
186
|
+
return card
|
|
187
|
+
|
|
188
|
+
def idcard(self, indent='+ '):
|
|
189
|
+
"""Returns a printable description of the current glove."""
|
|
190
|
+
card = "\n".join((
|
|
191
|
+
'{0}User = {1:s}',
|
|
192
|
+
'{0}Profile = {2!s}',
|
|
193
|
+
'{0}Vapp = {3:s}',
|
|
194
|
+
'{0}Vconf = {4:s}',
|
|
195
|
+
'{0}Configrc = {5:s}'
|
|
196
|
+
)).format(
|
|
197
|
+
indent,
|
|
198
|
+
self.user, self.profile, self.vapp, self.vconf, self.configrc
|
|
199
|
+
)
|
|
200
|
+
return card
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class ResearchGlove(Glove):
|
|
204
|
+
"""
|
|
205
|
+
The default glove as long as you do not need operational privileges.
|
|
206
|
+
Optional arguments are:
|
|
207
|
+
|
|
208
|
+
* mail
|
|
209
|
+
* profile (default is research)
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
_explicit = False
|
|
213
|
+
_footprint = dict(
|
|
214
|
+
info = 'Research glove',
|
|
215
|
+
attr = dict(
|
|
216
|
+
profile = dict(
|
|
217
|
+
optional = True,
|
|
218
|
+
values = ['research', 'tourist'],
|
|
219
|
+
default = 'research',
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def realkind(self):
|
|
226
|
+
return 'research'
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class OperGlove(Glove):
|
|
230
|
+
"""
|
|
231
|
+
The default glove if you need operational privileges.
|
|
232
|
+
Mandatory arguments are:
|
|
233
|
+
|
|
234
|
+
* user
|
|
235
|
+
* profile
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
_footprint = dict(
|
|
239
|
+
info = 'Operational glove',
|
|
240
|
+
attr = dict(
|
|
241
|
+
user = dict(
|
|
242
|
+
values = ['mxpt001']
|
|
243
|
+
),
|
|
244
|
+
profile = dict(
|
|
245
|
+
optional = False,
|
|
246
|
+
values = ['oper', 'dble', 'test', 'miroir'],
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def realkind(self):
|
|
253
|
+
return 'opuser'
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class UnitTestGlove(ResearchGlove):
|
|
257
|
+
"""A very special glove for unit-tests."""
|
|
258
|
+
|
|
259
|
+
_footprint = dict(
|
|
260
|
+
info = 'Unit-Test Glove',
|
|
261
|
+
attr = dict(
|
|
262
|
+
profile = dict(
|
|
263
|
+
optional = False,
|
|
264
|
+
values = ['utest'],
|
|
265
|
+
),
|
|
266
|
+
test_configrc = dict(
|
|
267
|
+
optional = False,
|
|
268
|
+
),
|
|
269
|
+
test_siteroot = dict(
|
|
270
|
+
optional = False,
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def __init__(self, *args, **kw):
|
|
276
|
+
super().__init__(*args, **kw)
|
|
277
|
+
self._siteroot = self.test_siteroot
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def configrc(self):
|
|
281
|
+
"""Returns the path of the default directory where ``.ini`` files are stored."""
|
|
282
|
+
return self.test_configrc
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Package dealing with various aspects of the VORTEX session organisation/layout.
|
|
3
|
+
|
|
4
|
+
It provides modules to keep track of all the input/output data handled during
|
|
5
|
+
a VORTEX session:
|
|
6
|
+
|
|
7
|
+
* the :mod:`dataflow` module defines the :class:`~dataflow.Section` class
|
|
8
|
+
and all the necessary class to gather and manipulate :class:`~dataflow.Section`
|
|
9
|
+
objects ;
|
|
10
|
+
* the :mod:`contexts` module is dedicated to the :class:`~contexts.Context`
|
|
11
|
+
class that provide a logical separation within VORTEX sessions. It mantains
|
|
12
|
+
the list of sections and environment variables ;
|
|
13
|
+
* the :mod:`monitor` module defines utility classes to monitor the state of an
|
|
14
|
+
ensemble of :class:`~dataflow.Section` objects.
|
|
15
|
+
|
|
16
|
+
It also provides modules that allows to create "standard" VORTEX's jobs:
|
|
17
|
+
|
|
18
|
+
* the :mod:`nodes` modules defines a bunch of classes that helps to organise
|
|
19
|
+
VORTEX jobs (creating tasks, families of tasks, ...);
|
|
20
|
+
* the :mod:`jobs` module focuses on the actual job generation and initialisation.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
#: No automatic export
|
|
25
|
+
__all__ = []
|
|
26
|
+
|
|
27
|
+
__tocinfoline__ = 'Package that helps organising a VORTEX session.'
|
vortex/layout/appconf.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This modules defines objects that any kind of configuration data
|
|
3
|
+
for jobs and nodes.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import collections.abc
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
from bronx.syntax.decorators import secure_getattr
|
|
10
|
+
|
|
11
|
+
from vortex.util.config import AppConfigStringDecoder
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigSet(collections.abc.MutableMapping):
|
|
15
|
+
"""Simple struct-like object that acts as a lower case dictionary.
|
|
16
|
+
|
|
17
|
+
Two syntax are available to add a new entry in a :class:`ConfigSet` object:
|
|
18
|
+
|
|
19
|
+
* ``ConfigSetObject.key = value``
|
|
20
|
+
* ``ConfigSetObject[key] = value``
|
|
21
|
+
|
|
22
|
+
Prior to being retrieved, entries are passed to a
|
|
23
|
+
:class:`vortex.util.config.AppConfigStringDecoder` object. It allows to
|
|
24
|
+
describe complex data types (see the :class:`vortex.util.config.AppConfigStringDecoder`
|
|
25
|
+
class documentation).
|
|
26
|
+
|
|
27
|
+
Some extra features are added on top of the
|
|
28
|
+
:class:`vortex.util.config.AppConfigStringDecoder` capabilities:
|
|
29
|
+
|
|
30
|
+
* If ``key`` ends with *_map*, ``value`` will be seen as a dictionary
|
|
31
|
+
* If ``key`` contains the words *geometry* or *geometries*, ``value``
|
|
32
|
+
will be converted to a :class:`vortex.data.geometries.Geometry` object
|
|
33
|
+
* If ``key`` ends with *_range*, ``value`` will be passed to the
|
|
34
|
+
:func:`footprints.util.rangex` function
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, *kargs, **kwargs):
|
|
39
|
+
super().__init__(*kargs, **kwargs)
|
|
40
|
+
self.__dict__['_internal'] = dict()
|
|
41
|
+
self.__dict__['_confdecoder'] = AppConfigStringDecoder(substitution_cb=self._internal.get)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _remap_key(key):
|
|
45
|
+
return key.lower()
|
|
46
|
+
|
|
47
|
+
def __iter__(self):
|
|
48
|
+
for k in self._internal.keys():
|
|
49
|
+
yield self._remap_key(k)
|
|
50
|
+
|
|
51
|
+
def __getitem__(self, key):
|
|
52
|
+
return self._confdecoder(self._internal[self._remap_key(key)])
|
|
53
|
+
|
|
54
|
+
def __setitem__(self, key, value):
|
|
55
|
+
if value is not None and isinstance(value, str):
|
|
56
|
+
# Support for old style dictionaries (compatibility)
|
|
57
|
+
if (key.endswith('_map') and not re.match(r'^dict\(.*\)$', value) and
|
|
58
|
+
not re.match(r'^\w+\(dict\(.*\)\)$', value)):
|
|
59
|
+
key = key[:-4]
|
|
60
|
+
if re.match(r'^\w+\(.*\)$', value):
|
|
61
|
+
value = re.sub(r'^(\w+)\((.*)\)$', r'\1(dict(\2))', value)
|
|
62
|
+
else:
|
|
63
|
+
value = 'dict(' + value + ')'
|
|
64
|
+
# Support for geometries (compatibility)
|
|
65
|
+
if (('geometry' in key or 'geometries' in key) and
|
|
66
|
+
(not re.match(r'^geometry\(.*\)$', value, flags=re.IGNORECASE))):
|
|
67
|
+
value = 'geometry(' + value + ')'
|
|
68
|
+
# Support for oldstyle range (compatibility)
|
|
69
|
+
if (key.endswith('_range') and not re.match(r'^rangex\(.*\)$', value) and
|
|
70
|
+
not re.match(r'^\w+\(rangex\(.*\)\)$', value)):
|
|
71
|
+
key = key[:-6]
|
|
72
|
+
if re.match(r'^\w+\(.*\)$', value):
|
|
73
|
+
value = re.sub(r'^(\w+)\((.*)\)$', r'\1(rangex(\2))', value)
|
|
74
|
+
else:
|
|
75
|
+
value = 'rangex(' + value + ')'
|
|
76
|
+
self._internal[self._remap_key(key)] = value
|
|
77
|
+
|
|
78
|
+
def __delitem__(self, key):
|
|
79
|
+
del self._internal[self._remap_key(key)]
|
|
80
|
+
|
|
81
|
+
def __len__(self):
|
|
82
|
+
return len(self._internal)
|
|
83
|
+
|
|
84
|
+
def clear(self):
|
|
85
|
+
self._internal.clear()
|
|
86
|
+
|
|
87
|
+
def __contains__(self, key):
|
|
88
|
+
return self._remap_key(key) in self._internal
|
|
89
|
+
|
|
90
|
+
@secure_getattr
|
|
91
|
+
def __getattr__(self, key):
|
|
92
|
+
if key in self:
|
|
93
|
+
return self[key]
|
|
94
|
+
else:
|
|
95
|
+
raise AttributeError('No such parameter <' + key + '>')
|
|
96
|
+
|
|
97
|
+
def __setattr__(self, attr, value):
|
|
98
|
+
self[attr] = value
|
|
99
|
+
|
|
100
|
+
def __delattr__(self, key):
|
|
101
|
+
if key in self:
|
|
102
|
+
del self[key]
|
|
103
|
+
else:
|
|
104
|
+
raise AttributeError('No such parameter <' + key + '>')
|
|
105
|
+
|
|
106
|
+
def copy(self):
|
|
107
|
+
newobj = self.__class__()
|
|
108
|
+
newobj.update(**self._internal)
|
|
109
|
+
return newobj
|