cgse-common 2025.0.3__py3-none-any.whl → 2025.0.5__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.
- {cgse_common-2025.0.3.dist-info → cgse_common-2025.0.5.dist-info}/METADATA +1 -1
- {cgse_common-2025.0.3.dist-info → cgse_common-2025.0.5.dist-info}/RECORD +14 -14
- egse/command.py +1 -1
- egse/control.py +0 -1
- egse/env.py +212 -28
- egse/hk.py +11 -13
- egse/protocol.py +1 -1
- egse/response.py +2 -1
- egse/services.py +4 -1
- egse/settings.py +182 -129
- egse/setup.py +38 -23
- egse/system.py +23 -17
- {cgse_common-2025.0.3.dist-info → cgse_common-2025.0.5.dist-info}/WHEEL +0 -0
- {cgse_common-2025.0.3.dist-info → cgse_common-2025.0.5.dist-info}/entry_points.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
egse/bits.py,sha256=1DPcC5R-8w-KLfbr2WLx-4Ezfwm6jpRMz-E5QfQ_YKw,11138
|
|
2
2
|
egse/calibration.py,sha256=DZ-LgEwamTWDukMqTr2JLCPt3M1lw6W0SfpHQfG7cXg,8989
|
|
3
|
-
egse/command.py,sha256=
|
|
3
|
+
egse/command.py,sha256=su35PU_0Bru1Es4up1lJFps9FTQpJ7xcxl9Y4w8suxA,22880
|
|
4
4
|
egse/config.py,sha256=Ib1Ddt0wFwRrioBvMs7Md-wgEEPdK3eaXCt0H7HJEow,14888
|
|
5
|
-
egse/control.py,sha256=
|
|
5
|
+
egse/control.py,sha256=cH0EA5mqaS3nQc35x1Sa0o2iM2Q4_hsrak6GOdcRMMU,12793
|
|
6
6
|
egse/decorators.py,sha256=M0PavrPcTwhWS6yLlEo-mOS20lcy90Mym7HUVzPNmhs,13404
|
|
7
7
|
egse/device.py,sha256=SIMROwB9YdNdFowTcZp1HW_el8LWVWDJ5tDUxs58YuY,8500
|
|
8
|
-
egse/env.py,sha256=
|
|
8
|
+
egse/env.py,sha256=RnqUb_LF_9pjrt2Vqf7b-Pa66XstiKyqgzA_HFlp7Mc,27777
|
|
9
9
|
egse/exceptions.py,sha256=Tc1xqUrWxV6SXxc9xB9viK_O-GVa_SpzqZUVEhZkwzA,1801
|
|
10
|
-
egse/hk.py,sha256=
|
|
10
|
+
egse/hk.py,sha256=5WMOGlx043XmEPj7ML1TkDzA8ZaTYLD-i5OcNUxE268,31163
|
|
11
11
|
egse/metrics.py,sha256=cZKMEe3OTT2uomj7vXjEl54JD0CStfEC4nCgS6U5YSM,3794
|
|
12
12
|
egse/mixin.py,sha256=J3yu5lPnm0grJqIm5LiacBUCZujJsdcKBNRaOQcMnNo,17345
|
|
13
13
|
egse/monitoring.py,sha256=-pwXqPoiNKzQYKQSpKddFlaPkCTJZYdxvG1d2MBN3l0,3033
|
|
@@ -16,21 +16,21 @@ egse/obsid.py,sha256=-HPuHApZrr3Nj1J2-qqnIiE814C-gm4FSHdM2afKdRY,5883
|
|
|
16
16
|
egse/persistence.py,sha256=Lx6LMJ1-dh8N43XF7tTM6RwD0sSETiGQ9DNqug-G-zQ,2160
|
|
17
17
|
egse/plugin.py,sha256=Fd_QnABm33_dLjaj1zDgEZr3RKy-N88U5Hz5OZm9Jj0,2684
|
|
18
18
|
egse/process.py,sha256=mQ2ojeL_9oE_QkMJlQDPd1290z0j2mOrGXrlrWtOtzI,16615
|
|
19
|
-
egse/protocol.py,sha256=
|
|
19
|
+
egse/protocol.py,sha256=G1HTU7xfS4xE1Oi1diDOTWQUTkPLyuooawWCOY6p-b0,23828
|
|
20
20
|
egse/proxy.py,sha256=pMKdnF62SXm0quLoKfgvK9GFkH2mLMB5fWNrZenfqQQ,18100
|
|
21
21
|
egse/reload.py,sha256=rDT0bC6RFeRhW38wSgFcxr30h8FvaKkoGp_OE-AwBB4,4388
|
|
22
22
|
egse/resource.py,sha256=VoB7BVrQULT_SJ1XioDzB59-uH47nUcN-KNVLvFxiFE,15163
|
|
23
|
-
egse/response.py,sha256=
|
|
24
|
-
egse/services.py,sha256=
|
|
23
|
+
egse/response.py,sha256=WFtWftovrmmn92NW4mhJjibMtCWteZmAzNuPLVsV-lg,2716
|
|
24
|
+
egse/services.py,sha256=rGS_5mSUFDw8wISEU_nuS9P9_XNtHvpViD9oSH9FqKo,7709
|
|
25
25
|
egse/services.yaml,sha256=p8QBF56zLI21iJ9skt65VlNz4rIqRoFfBTZxOIUZCZ4,1853
|
|
26
|
-
egse/settings.py,sha256=
|
|
26
|
+
egse/settings.py,sha256=E8i9nYyz6n931XP0BQu8xmtP3gHICBaSP4VMKL47IXs,15160
|
|
27
27
|
egse/settings.yaml,sha256=M6WMtTbNqNuXsdEf-OzKz0dAxkFTRabpugmIJ2HKyZk,290
|
|
28
|
-
egse/setup.py,sha256=
|
|
28
|
+
egse/setup.py,sha256=wFHqRQtDdkdbJeXH6hF28xUYnYhv-TYPIV-PunUoEow,43789
|
|
29
29
|
egse/state.py,sha256=ekcCZu_DZKkKYn-5iWG7ij7Aif2WYMNVs5h3cia-cVc,5352
|
|
30
|
-
egse/system.py,sha256=
|
|
30
|
+
egse/system.py,sha256=KCXz8eH3PNwLaB7ww3OCkK_tQoaTYhh2FEwX1mvN8RQ,48996
|
|
31
31
|
egse/version.py,sha256=-EMuiSn2eEp8QJX6csmBEu1m2yGqlXHJj2Hj49w6a2k,6217
|
|
32
32
|
egse/zmq_ser.py,sha256=2-nwVUBWZ3vvosKNmlWobHJrIJA2HlM3V5a63Gz2JY0,1819
|
|
33
|
-
cgse_common-2025.0.
|
|
34
|
-
cgse_common-2025.0.
|
|
35
|
-
cgse_common-2025.0.
|
|
36
|
-
cgse_common-2025.0.
|
|
33
|
+
cgse_common-2025.0.5.dist-info/METADATA,sha256=RBKcR3sDE90ugMy7tQy4c28Qfqae2f6vzEkCWwQ7kjA,2574
|
|
34
|
+
cgse_common-2025.0.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
+
cgse_common-2025.0.5.dist-info/entry_points.txt,sha256=E-KaQ9NGWAP1XvLHncNxq5oa22EAf9sOpBZWphXcxiE,34
|
|
36
|
+
cgse_common-2025.0.5.dist-info/RECORD,,
|
egse/command.py
CHANGED
egse/control.py
CHANGED
egse/env.py
CHANGED
|
@@ -40,30 +40,38 @@ WARNING:
|
|
|
40
40
|
from __future__ import annotations
|
|
41
41
|
|
|
42
42
|
__all__ = [
|
|
43
|
-
"
|
|
44
|
-
"set_data_storage_location",
|
|
45
|
-
"get_data_storage_location_env_name",
|
|
43
|
+
"env_var",
|
|
46
44
|
"get_conf_data_location",
|
|
47
|
-
"set_conf_data_location",
|
|
48
45
|
"get_conf_data_location_env_name",
|
|
49
46
|
"get_conf_repo_location",
|
|
50
|
-
"set_conf_repo_location",
|
|
51
47
|
"get_conf_repo_location_env_name",
|
|
48
|
+
"get_data_storage_location",
|
|
49
|
+
"get_data_storage_location_env_name",
|
|
50
|
+
"get_local_settings",
|
|
51
|
+
"get_local_settings_env_name",
|
|
52
52
|
"get_log_file_location",
|
|
53
|
-
"set_log_file_location",
|
|
54
53
|
"get_log_file_location_env_name",
|
|
55
|
-
"
|
|
54
|
+
"get_project_name",
|
|
55
|
+
"get_site_id",
|
|
56
|
+
"set_conf_data_location",
|
|
57
|
+
"set_conf_repo_location",
|
|
58
|
+
"set_data_storage_location",
|
|
56
59
|
"set_local_settings",
|
|
57
|
-
"
|
|
60
|
+
"set_log_file_location",
|
|
58
61
|
]
|
|
59
62
|
|
|
63
|
+
import contextlib
|
|
60
64
|
import os
|
|
61
65
|
import warnings
|
|
62
66
|
from pathlib import Path
|
|
63
67
|
|
|
64
68
|
from egse.system import all_logging_disabled
|
|
69
|
+
from egse.system import get_caller_info
|
|
65
70
|
from egse.system import ignore_m_warning
|
|
66
71
|
|
|
72
|
+
from rich.console import Console
|
|
73
|
+
console = Console(width=100)
|
|
74
|
+
|
|
67
75
|
# Every project shall have a PROJECT and a SITE_ID environment variable set. This variable will be used to
|
|
68
76
|
# create the other environment variables that are specific to the project.
|
|
69
77
|
|
|
@@ -121,11 +129,18 @@ class _Env:
|
|
|
121
129
|
self._env = {}
|
|
122
130
|
|
|
123
131
|
def set(self, key, value):
|
|
124
|
-
|
|
132
|
+
if value is None:
|
|
133
|
+
if key in self._env:
|
|
134
|
+
del self._env[key]
|
|
135
|
+
else:
|
|
136
|
+
self._env[key] = value
|
|
125
137
|
|
|
126
138
|
def get(self, key) -> str:
|
|
127
139
|
return self._env.get(key, NoValue())
|
|
128
140
|
|
|
141
|
+
def __rich__(self):
|
|
142
|
+
return self._env
|
|
143
|
+
|
|
129
144
|
|
|
130
145
|
_env = _Env()
|
|
131
146
|
|
|
@@ -157,20 +172,60 @@ def _check_no_value(var_name, value):
|
|
|
157
172
|
"""Raise a ValueError when the value for the variable is NoValue."""
|
|
158
173
|
if value == NoValue():
|
|
159
174
|
project = _env.get("PROJECT")
|
|
160
|
-
env_name = var_name if var_name
|
|
175
|
+
env_name = var_name if var_name in ("PROJECT", "SITE_ID") else f"{project}_{var_name}"
|
|
161
176
|
raise ValueError(
|
|
162
177
|
f"The environment variable {env_name} is not set. "
|
|
163
178
|
f"Please set the environment variable before proceeding."
|
|
164
179
|
)
|
|
165
180
|
|
|
166
181
|
|
|
182
|
+
def set_default_environment(
|
|
183
|
+
project: str,
|
|
184
|
+
site_id: str,
|
|
185
|
+
data_storage_location: str | Path,
|
|
186
|
+
conf_data_location: str | Path | None = None,
|
|
187
|
+
conf_repo_location: str | Path | None = None,
|
|
188
|
+
log_file_location: str | Path | None = None,
|
|
189
|
+
local_settings: str | Path | None = None,
|
|
190
|
+
):
|
|
191
|
+
set_project_name(project)
|
|
192
|
+
set_site_id(site_id)
|
|
193
|
+
set_data_storage_location(data_storage_location)
|
|
194
|
+
set_conf_data_location(conf_data_location)
|
|
195
|
+
set_conf_repo_location(conf_repo_location)
|
|
196
|
+
set_log_file_location(log_file_location)
|
|
197
|
+
set_local_settings(local_settings)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def get_project_name():
|
|
201
|
+
"""Get the PROJECT name. Return None when the PROJECT is not set."""
|
|
202
|
+
return _env.get("PROJECT") or None
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def set_project_name(name: str):
|
|
206
|
+
"""Set the environment variable PROJECT and its internal representation."""
|
|
207
|
+
os.environ["PROJECT"] = name
|
|
208
|
+
_env.set("PROJECT", name)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def get_site_id():
|
|
212
|
+
"""Get the SITE_ID. Return None if the SITE_ID is not set."""
|
|
213
|
+
return _env.get("SITE_ID") or None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def set_site_id(name: str):
|
|
217
|
+
"""Set the environment variable SITE_ID and its internal representation."""
|
|
218
|
+
os.environ["SITE_ID"] = name
|
|
219
|
+
_env.set("SITE_ID", name)
|
|
220
|
+
|
|
221
|
+
|
|
167
222
|
def get_data_storage_location_env_name() -> str:
|
|
168
223
|
"""Returns the name of the environment variable for the project."""
|
|
169
224
|
project = _env.get("PROJECT")
|
|
170
225
|
return f"{project}_DATA_STORAGE_LOCATION"
|
|
171
226
|
|
|
172
227
|
|
|
173
|
-
def set_data_storage_location(location: str | Path):
|
|
228
|
+
def set_data_storage_location(location: str | Path | None):
|
|
174
229
|
"""
|
|
175
230
|
Sets the environment variable and the internal representation to the given value.
|
|
176
231
|
|
|
@@ -179,14 +234,19 @@ def set_data_storage_location(location: str | Path):
|
|
|
179
234
|
"""
|
|
180
235
|
env_name = get_data_storage_location_env_name()
|
|
181
236
|
|
|
182
|
-
|
|
237
|
+
if location is None:
|
|
238
|
+
if env_name in os.environ:
|
|
239
|
+
del os.environ[env_name]
|
|
240
|
+
_env.set("DATA_STORAGE_LOCATION", None)
|
|
241
|
+
return
|
|
242
|
+
|
|
183
243
|
if not Path(location).exists():
|
|
184
244
|
warnings.warn(
|
|
185
245
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
186
246
|
)
|
|
187
247
|
|
|
188
|
-
os.environ[env_name] = location
|
|
189
|
-
_env.set('DATA_STORAGE_LOCATION', location)
|
|
248
|
+
os.environ[env_name] = str(location)
|
|
249
|
+
_env.set('DATA_STORAGE_LOCATION', str(location))
|
|
190
250
|
|
|
191
251
|
|
|
192
252
|
def get_data_storage_location(site_id: str = None) -> str:
|
|
@@ -213,6 +273,9 @@ def get_data_storage_location(site_id: str = None) -> str:
|
|
|
213
273
|
"""
|
|
214
274
|
global _env
|
|
215
275
|
|
|
276
|
+
project = _env.get("PROJECT")
|
|
277
|
+
_check_no_value("PROJECT", project)
|
|
278
|
+
|
|
216
279
|
site_id = site_id or _env.get("SITE_ID")
|
|
217
280
|
_check_no_value("SITE_ID", site_id)
|
|
218
281
|
|
|
@@ -230,7 +293,7 @@ def get_conf_data_location_env_name() -> str:
|
|
|
230
293
|
return f"{project}_CONF_DATA_LOCATION"
|
|
231
294
|
|
|
232
295
|
|
|
233
|
-
def set_conf_data_location(location: str | Path):
|
|
296
|
+
def set_conf_data_location(location: str | Path | None):
|
|
234
297
|
"""
|
|
235
298
|
Sets the environment variable and the internal representation to the given value.
|
|
236
299
|
|
|
@@ -240,7 +303,12 @@ def set_conf_data_location(location: str | Path):
|
|
|
240
303
|
|
|
241
304
|
env_name = get_conf_data_location_env_name()
|
|
242
305
|
|
|
243
|
-
|
|
306
|
+
if location is None:
|
|
307
|
+
if env_name in os.environ:
|
|
308
|
+
del os.environ[env_name]
|
|
309
|
+
_env.set("CONF_DATA_LOCATION", None)
|
|
310
|
+
return
|
|
311
|
+
|
|
244
312
|
if not Path(location).exists():
|
|
245
313
|
warnings.warn(
|
|
246
314
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
@@ -293,7 +361,7 @@ def get_log_file_location_env_name():
|
|
|
293
361
|
return f"{project}_LOG_FILE_LOCATION"
|
|
294
362
|
|
|
295
363
|
|
|
296
|
-
def set_log_file_location(location: str | Path):
|
|
364
|
+
def set_log_file_location(location: str | Path | None):
|
|
297
365
|
"""
|
|
298
366
|
Sets the environment variable and the internal representation to the given value.
|
|
299
367
|
|
|
@@ -303,7 +371,12 @@ def set_log_file_location(location: str | Path):
|
|
|
303
371
|
|
|
304
372
|
env_name = get_log_file_location_env_name()
|
|
305
373
|
|
|
306
|
-
|
|
374
|
+
if location is None:
|
|
375
|
+
if env_name in os.environ:
|
|
376
|
+
del os.environ[env_name]
|
|
377
|
+
_env.set("LOG_FILE_LOCATION", None)
|
|
378
|
+
return
|
|
379
|
+
|
|
307
380
|
if not Path(location).exists():
|
|
308
381
|
warnings.warn(
|
|
309
382
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
@@ -355,17 +428,24 @@ def get_local_settings_env_name() -> str:
|
|
|
355
428
|
return f"{project}_LOCAL_SETTINGS"
|
|
356
429
|
|
|
357
430
|
|
|
358
|
-
def set_local_settings(path: str | Path):
|
|
431
|
+
def set_local_settings(path: str | Path | None):
|
|
359
432
|
"""
|
|
360
433
|
Sets the environment variable and the internal representation to the given value.
|
|
361
434
|
|
|
435
|
+
When the path is set to None, the environment variable will be unset.
|
|
436
|
+
|
|
362
437
|
Warnings:
|
|
363
438
|
Issues a warning when the given path doesn't exist.
|
|
364
439
|
"""
|
|
365
440
|
|
|
366
441
|
env_name = get_local_settings_env_name()
|
|
367
442
|
|
|
368
|
-
|
|
443
|
+
if path is None:
|
|
444
|
+
if env_name in os.environ:
|
|
445
|
+
del os.environ[env_name]
|
|
446
|
+
_env.set('LOCAL_SETTINGS', None)
|
|
447
|
+
return
|
|
448
|
+
|
|
369
449
|
if not Path(path).exists():
|
|
370
450
|
warnings.warn(
|
|
371
451
|
f"The location you provided for the environment variable {env_name} doesn't exist: {path}."
|
|
@@ -389,34 +469,61 @@ def get_local_settings() -> str:
|
|
|
389
469
|
return local_settings or None
|
|
390
470
|
|
|
391
471
|
|
|
472
|
+
def has_conf_repo_location() -> bool:
|
|
473
|
+
location = _env.get("CONF_REPO_LOCATION")
|
|
474
|
+
return True if location else False
|
|
475
|
+
|
|
476
|
+
|
|
392
477
|
def get_conf_repo_location_env_name() -> str:
|
|
393
478
|
"""Returns the name of the environment variable for the project."""
|
|
394
479
|
project = _env.get("PROJECT")
|
|
395
480
|
return f"{project}_CONF_REPO_LOCATION"
|
|
396
481
|
|
|
397
482
|
|
|
398
|
-
def get_conf_repo_location() -> str:
|
|
399
|
-
"""
|
|
483
|
+
def get_conf_repo_location() -> str | None:
|
|
484
|
+
"""
|
|
485
|
+
Returns the fully qualified name of the location of the repository with
|
|
486
|
+
configuration and calibration data.
|
|
487
|
+
|
|
488
|
+
Returns None if no environment variable was defined or if the location doesn't exist.
|
|
489
|
+
In both cases a Warning is issued.
|
|
490
|
+
"""
|
|
400
491
|
|
|
401
492
|
location = _env.get("CONF_REPO_LOCATION")
|
|
402
493
|
|
|
403
|
-
if location
|
|
494
|
+
if location in (None, NoValue()):
|
|
495
|
+
warnings.warn(
|
|
496
|
+
f"The environment variable for the configuration data repository is "
|
|
497
|
+
f"not defined ({get_conf_repo_location_env_name()})."
|
|
498
|
+
)
|
|
499
|
+
return None
|
|
500
|
+
|
|
501
|
+
if not Path(location).exists():
|
|
404
502
|
warnings.warn(f"The location of the configuration data repository doesn't exist: {location}.")
|
|
503
|
+
return None
|
|
405
504
|
|
|
406
|
-
return location
|
|
505
|
+
return location
|
|
407
506
|
|
|
408
507
|
|
|
409
|
-
def set_conf_repo_location(location: str):
|
|
508
|
+
def set_conf_repo_location(location: str | Path | None):
|
|
410
509
|
"""
|
|
411
510
|
Sets the environment variable and the internal representation to the given value.
|
|
412
511
|
|
|
512
|
+
When the location is None, the environment variable will be unset and its internal
|
|
513
|
+
representation will be NoValue().
|
|
514
|
+
|
|
413
515
|
Warnings:
|
|
414
516
|
Issues a warning when the given location doesn't exist.
|
|
415
517
|
"""
|
|
416
518
|
|
|
417
519
|
env_name = get_conf_repo_location_env_name()
|
|
418
520
|
|
|
419
|
-
|
|
521
|
+
if location is None:
|
|
522
|
+
if env_name in os.environ:
|
|
523
|
+
del os.environ[env_name]
|
|
524
|
+
_env.set("CONF_REPO_LOCATION", None)
|
|
525
|
+
return
|
|
526
|
+
|
|
420
527
|
if not Path(location).exists():
|
|
421
528
|
warnings.warn(
|
|
422
529
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
@@ -426,7 +533,69 @@ def set_conf_repo_location(location: str):
|
|
|
426
533
|
_env.set('CONF_REPO_LOCATION', location)
|
|
427
534
|
|
|
428
535
|
|
|
429
|
-
|
|
536
|
+
def print_env():
|
|
537
|
+
"""
|
|
538
|
+
Prints out the mandatory and known environment variables at the time of the
|
|
539
|
+
function call. The function and lineno is also printed for information.
|
|
540
|
+
"""
|
|
541
|
+
col_width = 30
|
|
542
|
+
|
|
543
|
+
console = Console(width=200)
|
|
544
|
+
|
|
545
|
+
with warnings.catch_warnings():
|
|
546
|
+
warnings.simplefilter("ignore")
|
|
547
|
+
caller_info = get_caller_info(level=2)
|
|
548
|
+
console.print(f"[b]Environment as in {caller_info.filename}:{caller_info.lineno}[/]")
|
|
549
|
+
console.print(f" {'PROJECT':{col_width}s}: {get_project_name()}")
|
|
550
|
+
console.print(f" {'SITE_ID':{col_width}s}: {get_site_id()}")
|
|
551
|
+
console.print(f" {get_data_storage_location_env_name():{col_width}s}: {get_data_storage_location()}")
|
|
552
|
+
console.print(f" {get_log_file_location_env_name():{col_width}s}: {get_log_file_location()}")
|
|
553
|
+
console.print(f" {get_conf_data_location_env_name():{col_width}s}: {get_conf_data_location()}")
|
|
554
|
+
console.print(f" {get_conf_repo_location_env_name():{col_width}s}: {get_conf_repo_location()}")
|
|
555
|
+
console.print(f" {get_local_settings_env_name():{col_width}s}: {get_local_settings()}")
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
@contextlib.contextmanager
|
|
559
|
+
def env_var(**kwargs):
|
|
560
|
+
"""
|
|
561
|
+
Context manager to run some code that need alternate settings for environment variables.
|
|
562
|
+
This will automatically initialize the CGSE environment upon entry and re-initialize upon exit.
|
|
563
|
+
|
|
564
|
+
NOTE: This context manager is different from the one from `egse.system` because of the CGSE environment changes.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
**kwargs: dictionary with environment variables that are needed
|
|
568
|
+
|
|
569
|
+
Examples:
|
|
570
|
+
|
|
571
|
+
>>> from egse.env import env_var
|
|
572
|
+
>>> with env_var(PLATO_DATA_STORAGE_LOCATION="/Users/rik/data"):
|
|
573
|
+
... # do stuff that needs these alternate setting
|
|
574
|
+
... pass
|
|
575
|
+
|
|
576
|
+
"""
|
|
577
|
+
saved_env = {}
|
|
578
|
+
|
|
579
|
+
for k, v in kwargs.items():
|
|
580
|
+
saved_env[k] = os.environ.get(k)
|
|
581
|
+
if v is None:
|
|
582
|
+
if k in os.environ:
|
|
583
|
+
del os.environ[k]
|
|
584
|
+
else:
|
|
585
|
+
os.environ[k] = v
|
|
586
|
+
|
|
587
|
+
initialize()
|
|
588
|
+
|
|
589
|
+
yield
|
|
590
|
+
|
|
591
|
+
for k, v in saved_env.items():
|
|
592
|
+
if v is None:
|
|
593
|
+
if k in os.environ:
|
|
594
|
+
del os.environ[k]
|
|
595
|
+
else:
|
|
596
|
+
os.environ[k] = v
|
|
597
|
+
|
|
598
|
+
initialize()
|
|
430
599
|
|
|
431
600
|
|
|
432
601
|
def main(args: list | None = None): # pragma: no cover
|
|
@@ -507,8 +676,10 @@ def main(args: list | None = None): # pragma: no cover
|
|
|
507
676
|
location = get_data_storage_location()
|
|
508
677
|
if not Path(location).exists():
|
|
509
678
|
if args.mkdir:
|
|
510
|
-
rich.print(f" [green]⟶ Creating data storage location: {location}[/]")
|
|
679
|
+
rich.print(f" [green]⟶ Creating data storage location: {location} (+ daily + obs)[/]")
|
|
511
680
|
Path(location).mkdir(parents=True)
|
|
681
|
+
(Path(location) / "daily").mkdir()
|
|
682
|
+
(Path(location) / "obs").mkdir()
|
|
512
683
|
else:
|
|
513
684
|
rich.print(" [red]⟶ ERROR: The data storage location doesn't exist![/]")
|
|
514
685
|
else:
|
|
@@ -530,6 +701,16 @@ def main(args: list | None = None): # pragma: no cover
|
|
|
530
701
|
except ValueError as exc:
|
|
531
702
|
rich.print(f" get_conf_data_location() = [red]{exc}[/]")
|
|
532
703
|
|
|
704
|
+
try:
|
|
705
|
+
rich.print(f" {get_conf_repo_location() = }", flush=True, end="")
|
|
706
|
+
location = get_conf_repo_location()
|
|
707
|
+
if location is None or not Path(location).exists():
|
|
708
|
+
rich.print(" [red]⟶ ERROR: The configuration repository location doesn't exist![/]")
|
|
709
|
+
else:
|
|
710
|
+
rich.print()
|
|
711
|
+
except ValueError as exc:
|
|
712
|
+
rich.print(f" get_conf_repo_location() = [red]{exc}[/]")
|
|
713
|
+
|
|
533
714
|
try:
|
|
534
715
|
rich.print(f" {get_log_file_location() = }", flush=True, end="")
|
|
535
716
|
location = get_log_file_location()
|
|
@@ -613,6 +794,9 @@ def main(args: list | None = None): # pragma: no cover
|
|
|
613
794
|
# PLATO_COMMON_EGSE_PATH - YES
|
|
614
795
|
|
|
615
796
|
|
|
797
|
+
ignore_m_warning('egse.env')
|
|
798
|
+
|
|
799
|
+
|
|
616
800
|
if __name__ == "__main__":
|
|
617
801
|
import sys
|
|
618
802
|
main(sys.argv[1:])
|
egse/hk.py
CHANGED
|
@@ -16,9 +16,9 @@ import dateutil.parser as date_parser
|
|
|
16
16
|
import numpy as np
|
|
17
17
|
from egse.config import find_files
|
|
18
18
|
from egse.env import get_data_storage_location
|
|
19
|
+
from egse.env import get_site_id
|
|
19
20
|
from egse.obsid import ObservationIdentifier
|
|
20
21
|
from egse.obsid import obsid_from_storage
|
|
21
|
-
from egse.settings import Settings
|
|
22
22
|
from egse.setup import Setup
|
|
23
23
|
from egse.setup import SetupError
|
|
24
24
|
from egse.setup import load_setup
|
|
@@ -30,8 +30,6 @@ from egse.system import time_since_epoch_1958
|
|
|
30
30
|
|
|
31
31
|
_LOGGER = logging.getLogger(__name__)
|
|
32
32
|
|
|
33
|
-
SITE_ID = Settings.load("SITE").ID
|
|
34
|
-
|
|
35
33
|
|
|
36
34
|
class TmDictionaryColumns(str, Enum):
|
|
37
35
|
""" Enumeration of the relevant columns in the TM dictionary spreadsheet.
|
|
@@ -50,7 +48,7 @@ class TmDictionaryColumns(str, Enum):
|
|
|
50
48
|
STORAGE_MNEMONIC = "Storage mnemonic"
|
|
51
49
|
CORRECT_HK_NAMES = "CAM EGSE mnemonic"
|
|
52
50
|
ORIGINAL_EGSE_HK_NAMES = "Original name in EGSE"
|
|
53
|
-
SYNOPTICS_ORIGIN = f"Origin of synoptics at {
|
|
51
|
+
SYNOPTICS_ORIGIN = f"Origin of synoptics at {get_site_id()}"
|
|
54
52
|
TIMESTAMP_NAMES = "Name of corresponding timestamp"
|
|
55
53
|
DESCRIPTION = "Description"
|
|
56
54
|
DASHBOARD = "MON screen"
|
|
@@ -120,7 +118,7 @@ def get_housekeeping(hk_name: str, obsid: Union[ObservationIdentifier, str, int]
|
|
|
120
118
|
try:
|
|
121
119
|
return _get_housekeeping_obsid(hk_name, data_dir, obsid=obsid, time_window=time_window, setup=setup)
|
|
122
120
|
except (ValueError, StopIteration, FileNotFoundError) as exc:
|
|
123
|
-
raise HKError(f"No HK found for {hk_name} for obsid {obsid} at {
|
|
121
|
+
raise HKError(f"No HK found for {hk_name} for obsid {obsid} at {get_site_id()}") from exc
|
|
124
122
|
|
|
125
123
|
# Specified OD
|
|
126
124
|
|
|
@@ -129,14 +127,14 @@ def get_housekeeping(hk_name: str, obsid: Union[ObservationIdentifier, str, int]
|
|
|
129
127
|
try:
|
|
130
128
|
return _get_housekeeping_od(hk_name, data_dir, od=od, time_window=time_window, setup=setup)
|
|
131
129
|
except (ValueError, StopIteration, FileNotFoundError) as exc:
|
|
132
|
-
raise HKError(f"No HK found for {hk_name} for OD {od} at {
|
|
130
|
+
raise HKError(f"No HK found for {hk_name} for OD {od} at {get_site_id()}") from exc
|
|
133
131
|
|
|
134
132
|
# Didn't specify neither the obsid nor the OD
|
|
135
133
|
|
|
136
134
|
try:
|
|
137
135
|
return _get_housekeeping_daily(hk_name, data_dir, time_window=time_window, setup=setup)
|
|
138
136
|
except (ValueError, StopIteration, FileNotFoundError) as exc:
|
|
139
|
-
raise HKError(f"No HK found for {hk_name} for today at {
|
|
137
|
+
raise HKError(f"No HK found for {hk_name} for today at {get_site_id()}") from exc
|
|
140
138
|
|
|
141
139
|
|
|
142
140
|
def _get_housekeeping(hk_name: str, timestamp_name: str, hk_dir: str, files, time_window: int = None):
|
|
@@ -272,7 +270,7 @@ def _get_housekeeping_od(hk_name: str, data_dir, od: str, time_window: int = Non
|
|
|
272
270
|
raise HKError(f"Cannot determine which EGSE component generated HK parameter {hk_name}")
|
|
273
271
|
|
|
274
272
|
hk_dir += f"{od}/"
|
|
275
|
-
hk_files = [f"{od}_{
|
|
273
|
+
hk_files = [f"{od}_{get_site_id()}_{origin}.csv"]
|
|
276
274
|
|
|
277
275
|
return _get_housekeeping(hk_name, timestamp_name, hk_dir, hk_files, time_window=time_window)
|
|
278
276
|
|
|
@@ -326,7 +324,7 @@ def _get_housekeeping_obsid(hk_name: str, data_dir, obsid: Union[ObservationIden
|
|
|
326
324
|
|
|
327
325
|
if len(hk_files) == 0:
|
|
328
326
|
|
|
329
|
-
raise HKError(f"No HK found for the {origin} at {
|
|
327
|
+
raise HKError(f"No HK found for the {origin} at {get_site_id()} for obsid {obsid}")
|
|
330
328
|
|
|
331
329
|
hk_files = [hk_files[-1].name]
|
|
332
330
|
|
|
@@ -387,7 +385,7 @@ def _get_housekeeping_daily(hk_name: str, data_dir, time_window: int = None, set
|
|
|
387
385
|
|
|
388
386
|
timestamp = datetime.datetime.now(tz=datetime.timezone.utc).strftime("%Y%m%d")
|
|
389
387
|
hk_dir += f"{timestamp}/"
|
|
390
|
-
filename = f"{timestamp}_{
|
|
388
|
+
filename = f"{timestamp}_{get_site_id()}_{origin}.csv"
|
|
391
389
|
|
|
392
390
|
timestamp_index, hk_index = get_indices(hk_dir + filename, hk_name, timestamp_name)
|
|
393
391
|
return get_last_non_empty(hk_dir + filename, timestamp_index, hk_index)
|
|
@@ -412,7 +410,7 @@ def _get_housekeeping_daily(hk_name: str, data_dir, time_window: int = None, set
|
|
|
412
410
|
|
|
413
411
|
# Determine which columns will be needed from which file
|
|
414
412
|
|
|
415
|
-
filename = f"{start_od}/{start_od}_{
|
|
413
|
+
filename = f"{start_od}/{start_od}_{get_site_id()}_{origin}.csv"
|
|
416
414
|
|
|
417
415
|
if Path(hk_dir + filename).exists():
|
|
418
416
|
|
|
@@ -456,7 +454,7 @@ def _get_housekeeping_daily(hk_name: str, data_dir, time_window: int = None, set
|
|
|
456
454
|
while day <= last_day:
|
|
457
455
|
|
|
458
456
|
od = f"{day.year}{day.month:02d}{day.day:02d}"
|
|
459
|
-
filename = f"{od}/{od}_{
|
|
457
|
+
filename = f"{od}/{od}_{get_site_id()}_{origin}.csv"
|
|
460
458
|
|
|
461
459
|
if Path(hk_dir + filename).exists():
|
|
462
460
|
|
|
@@ -684,7 +682,7 @@ def read_conversion_dict(storage_mnemonic: str, use_site: bool = False, setup: O
|
|
|
684
682
|
|
|
685
683
|
if use_site:
|
|
686
684
|
|
|
687
|
-
th_prefix = f"G{
|
|
685
|
+
th_prefix = f"G{get_site_id()}_"
|
|
688
686
|
|
|
689
687
|
th_conversion_dict = {}
|
|
690
688
|
|
egse/protocol.py
CHANGED
|
@@ -20,7 +20,7 @@ from prometheus_client import Summary
|
|
|
20
20
|
from egse.command import Command
|
|
21
21
|
from egse.command import CommandExecution
|
|
22
22
|
from egse.control import ControlServer
|
|
23
|
-
from egse.
|
|
23
|
+
from egse.response import Failure
|
|
24
24
|
from egse.device import DeviceConnectionObserver
|
|
25
25
|
from egse.system import format_datetime
|
|
26
26
|
from egse.zmq_ser import bind_address
|
egse/response.py
CHANGED
egse/services.py
CHANGED
|
@@ -10,6 +10,7 @@ logging levels, or to quit the control server in a controlled way.
|
|
|
10
10
|
|
|
11
11
|
import inspect
|
|
12
12
|
import logging
|
|
13
|
+
from pathlib import Path
|
|
13
14
|
|
|
14
15
|
from egse.command import ClientServerCommand
|
|
15
16
|
from egse.control import ControlServer
|
|
@@ -22,7 +23,9 @@ from egse.zmq_ser import connect_address
|
|
|
22
23
|
|
|
23
24
|
LOGGER = logging.getLogger(__name__)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
HERE = Path(__file__).parent
|
|
27
|
+
|
|
28
|
+
SERVICE_SETTINGS = Settings.load(filename=str(HERE / "services.yaml"))
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
class ServiceCommand(ClientServerCommand):
|
egse/settings.py
CHANGED
|
@@ -8,7 +8,7 @@ settings are loaded from a file called ``settings.yaml``. The default yaml confi
|
|
|
8
8
|
located in the same directory as this module.
|
|
9
9
|
|
|
10
10
|
The YAML file is read and the configuration parameters for the given group are
|
|
11
|
-
|
|
11
|
+
available as instance variables of the returned class.
|
|
12
12
|
|
|
13
13
|
The intended use is as follows:
|
|
14
14
|
|
|
@@ -54,24 +54,27 @@ If that file is located at a specific location, also use the ``location=`` keywo
|
|
|
54
54
|
The above code will read the complete YAML file, i.e. all the groups into a dictionary.
|
|
55
55
|
|
|
56
56
|
"""
|
|
57
|
+
from __future__ import annotations
|
|
57
58
|
|
|
58
59
|
import logging
|
|
59
|
-
import os
|
|
60
|
-
import pathlib
|
|
61
60
|
import re
|
|
61
|
+
from pathlib import Path
|
|
62
|
+
from typing import Any
|
|
62
63
|
|
|
63
64
|
import yaml # This module is provided by the pip package PyYaml - pip install pyyaml
|
|
64
65
|
|
|
65
66
|
from egse.env import get_local_settings
|
|
66
67
|
from egse.env import get_local_settings_env_name
|
|
67
68
|
from egse.exceptions import FileIsEmptyError
|
|
68
|
-
from egse.system import
|
|
69
|
-
from egse.system import
|
|
69
|
+
from egse.system import attrdict
|
|
70
|
+
from egse.system import get_package_location
|
|
70
71
|
from egse.system import ignore_m_warning
|
|
71
72
|
from egse.system import recursive_dict_update
|
|
72
73
|
|
|
73
74
|
_LOGGER = logging.getLogger(__name__)
|
|
74
75
|
|
|
76
|
+
_HERE = Path(__file__).resolve().parent
|
|
77
|
+
|
|
75
78
|
|
|
76
79
|
class SettingsError(Exception):
|
|
77
80
|
pass
|
|
@@ -109,6 +112,107 @@ SAFE_LOADER.add_implicit_resolver(
|
|
|
109
112
|
list(u'-+0123456789.'))
|
|
110
113
|
|
|
111
114
|
|
|
115
|
+
def get_settings_locations(location: str | Path = None, filename: str = "settings.yaml") -> list[Path]:
|
|
116
|
+
|
|
117
|
+
yaml_locations: set[Path] = set()
|
|
118
|
+
|
|
119
|
+
if location is None:
|
|
120
|
+
package_locations = get_package_location("egse") # `egse` is a namespace
|
|
121
|
+
|
|
122
|
+
for package_location in package_locations:
|
|
123
|
+
if (package_location / filename).exists():
|
|
124
|
+
yaml_locations.add(package_location)
|
|
125
|
+
|
|
126
|
+
yaml_locations.add(_HERE)
|
|
127
|
+
_LOGGER.debug(f"yaml_locations in Settings.load(): {yaml_locations}")
|
|
128
|
+
|
|
129
|
+
else:
|
|
130
|
+
|
|
131
|
+
package_location = Path(location).resolve()
|
|
132
|
+
if (package_location / filename).exists():
|
|
133
|
+
yaml_locations.add(package_location)
|
|
134
|
+
else:
|
|
135
|
+
_LOGGER.warning(f"No '{filename}' file found at {package_location}.")
|
|
136
|
+
|
|
137
|
+
return list(yaml_locations)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def load_global_settings(locations, filename: str = "settings.yaml", force: bool = False) -> attrdict:
|
|
141
|
+
|
|
142
|
+
global_settings = attrdict()
|
|
143
|
+
|
|
144
|
+
for yaml_location in locations:
|
|
145
|
+
try:
|
|
146
|
+
yaml_document = read_configuration_file(str(yaml_location / filename), force=force)
|
|
147
|
+
recursive_dict_update(global_settings, yaml_document)
|
|
148
|
+
except FileNotFoundError as exc:
|
|
149
|
+
raise SettingsError(
|
|
150
|
+
f"Filename {filename} not found at location {locations}."
|
|
151
|
+
) from exc
|
|
152
|
+
|
|
153
|
+
return global_settings
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def load_local_settings(force: bool = False):
|
|
157
|
+
|
|
158
|
+
local_settings = {}
|
|
159
|
+
try:
|
|
160
|
+
local_settings_location = get_local_settings()
|
|
161
|
+
|
|
162
|
+
if local_settings_location:
|
|
163
|
+
_LOGGER.debug(f"Using {local_settings_location} to update global settings.")
|
|
164
|
+
try:
|
|
165
|
+
yaml_document_local = read_configuration_file(local_settings_location, force=force)
|
|
166
|
+
if yaml_document_local is None:
|
|
167
|
+
raise FileIsEmptyError()
|
|
168
|
+
local_settings = attrdict(
|
|
169
|
+
{name: value for name, value in yaml_document_local.items()}
|
|
170
|
+
)
|
|
171
|
+
except FileNotFoundError as exc:
|
|
172
|
+
raise SettingsError(
|
|
173
|
+
f"Local settings YAML file '{local_settings_location}' not found. "
|
|
174
|
+
f"Check your environment variable {get_local_settings_env_name()}."
|
|
175
|
+
) from exc
|
|
176
|
+
except FileIsEmptyError:
|
|
177
|
+
_LOGGER.warning(
|
|
178
|
+
f"Local settings YAML file '{local_settings_location}' is empty. "
|
|
179
|
+
f"No local settings were loaded.")
|
|
180
|
+
|
|
181
|
+
except KeyError as exc:
|
|
182
|
+
_LOGGER.warning(f"The environment variable {get_local_settings_env_name()} is not defined. (from "
|
|
183
|
+
f"{exc.__class__.__name__}: {exc})")
|
|
184
|
+
|
|
185
|
+
return local_settings
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def read_configuration_file(filename: str, *, force=False):
|
|
189
|
+
"""
|
|
190
|
+
Read the YAML input configuration file. The configuration file is only read
|
|
191
|
+
once and memoized as load optimization.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
filename (str): the fully qualified filename of the YAML file
|
|
195
|
+
force (bool): force reloading the file
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
a dictionary containing all the configuration settings from the YAML file.
|
|
199
|
+
"""
|
|
200
|
+
if force or not Settings.is_memoized(filename):
|
|
201
|
+
|
|
202
|
+
_LOGGER.debug(f"Parsing YAML configuration file {filename}.")
|
|
203
|
+
|
|
204
|
+
with open(filename, "r") as stream:
|
|
205
|
+
try:
|
|
206
|
+
yaml_document = yaml.load(stream, Loader=SAFE_LOADER)
|
|
207
|
+
except yaml.YAMLError as exc:
|
|
208
|
+
_LOGGER.error(exc)
|
|
209
|
+
raise SettingsError(f"Error loading YAML document {filename}") from exc
|
|
210
|
+
|
|
211
|
+
Settings.add_memoized(filename, yaml_document)
|
|
212
|
+
|
|
213
|
+
return Settings.get_memoized(filename) or {}
|
|
214
|
+
|
|
215
|
+
|
|
112
216
|
class Settings:
|
|
113
217
|
"""
|
|
114
218
|
The Settings class provides a load() method that loads configuration settings for a group
|
|
@@ -116,9 +220,7 @@ class Settings:
|
|
|
116
220
|
"""
|
|
117
221
|
|
|
118
222
|
__memoized_yaml = {} # Memoized settings yaml files
|
|
119
|
-
|
|
120
223
|
__profile = False # Used for profiling methods and functions
|
|
121
|
-
__simulation = False # Use simulation mode where applicable and possible
|
|
122
224
|
|
|
123
225
|
LOG_FORMAT_DEFAULT = "%(levelname)s:%(module)s:%(lineno)d:%(message)s"
|
|
124
226
|
LOG_FORMAT_FULL = "%(asctime)23s:%(levelname)8s:%(lineno)5d:%(name)-20s: %(message)s"
|
|
@@ -132,40 +234,35 @@ class Settings:
|
|
|
132
234
|
LOG_FORMAT_DATE = "%d/%m/%Y %H:%M:%S"
|
|
133
235
|
|
|
134
236
|
@classmethod
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
Read the YAML input configuration file. The configuration file is only read
|
|
138
|
-
once and memoized as load optimization.
|
|
139
|
-
|
|
140
|
-
Args:
|
|
141
|
-
filename (str): the fully qualified filename of the YAML file
|
|
142
|
-
force (bool): force reloading the file
|
|
237
|
+
def get_memoized_locations(cls) -> list:
|
|
238
|
+
return list(cls.__memoized_yaml.keys())
|
|
143
239
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if force or filename not in cls.__memoized_yaml:
|
|
240
|
+
@classmethod
|
|
241
|
+
def is_memoized(cls, filename: str) -> bool:
|
|
242
|
+
return filename in cls.__memoized_yaml
|
|
148
243
|
|
|
149
|
-
|
|
244
|
+
@classmethod
|
|
245
|
+
def add_memoized(cls, filename: str, yaml_document: Any):
|
|
246
|
+
cls.__memoized_yaml[filename] = yaml_document
|
|
150
247
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
except yaml.YAMLError as exc:
|
|
155
|
-
_LOGGER.error(exc)
|
|
156
|
-
raise SettingsError(f"Error loading YAML document {filename}") from exc
|
|
248
|
+
@classmethod
|
|
249
|
+
def get_memoized(cls, filename: str):
|
|
250
|
+
return cls.__memoized_yaml.get(filename)
|
|
157
251
|
|
|
158
|
-
|
|
252
|
+
@classmethod
|
|
253
|
+
def clear_memoized(cls):
|
|
254
|
+
cls.__memoized_yaml.clear()
|
|
159
255
|
|
|
160
|
-
|
|
256
|
+
@classmethod
|
|
257
|
+
def set_profiling(cls, flag):
|
|
258
|
+
cls.__profile = flag
|
|
161
259
|
|
|
162
260
|
@classmethod
|
|
163
|
-
def
|
|
164
|
-
return cls.
|
|
261
|
+
def profiling(cls):
|
|
262
|
+
return cls.__profile
|
|
165
263
|
|
|
166
264
|
@classmethod
|
|
167
|
-
def load(cls, group_name=None, filename="settings.yaml", location=None, *, force=False,
|
|
168
|
-
add_local_settings=True):
|
|
265
|
+
def load(cls, group_name=None, filename="settings.yaml", location=None, *, force=False, add_local_settings=True):
|
|
169
266
|
"""
|
|
170
267
|
Load the settings for the given group from YAML configuration file.
|
|
171
268
|
When no group is provided, the complete configuration is returned.
|
|
@@ -186,7 +283,7 @@ class Settings:
|
|
|
186
283
|
Args:
|
|
187
284
|
group_name (str): the name of one of the main groups from the YAML file
|
|
188
285
|
filename (str): the name of the YAML file to read
|
|
189
|
-
location (str): the path to the location of the YAML file
|
|
286
|
+
location (str, Path): the path to the location of the YAML file
|
|
190
287
|
force (bool): force reloading the file
|
|
191
288
|
add_local_settings (bool): update the Settings with site specific local settings
|
|
192
289
|
|
|
@@ -197,99 +294,58 @@ class Settings:
|
|
|
197
294
|
a SettingsError when the group is not defined in the YAML file.
|
|
198
295
|
"""
|
|
199
296
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if location is None:
|
|
203
|
-
|
|
204
|
-
# Check if the yaml file is located at the location of the caller,
|
|
205
|
-
# if not, use the file that is located where the Settings module is located.
|
|
206
|
-
|
|
207
|
-
caller_dir = get_caller_info(level=2).filename
|
|
208
|
-
caller_dir = pathlib.Path(caller_dir).resolve().parent
|
|
209
|
-
|
|
210
|
-
if (caller_dir / filename).is_file():
|
|
211
|
-
yaml_location = caller_dir
|
|
212
|
-
else:
|
|
213
|
-
yaml_location = _THIS_FILE_LOCATION
|
|
214
|
-
else:
|
|
215
|
-
|
|
216
|
-
# The location was given as an argument
|
|
217
|
-
|
|
218
|
-
yaml_location = pathlib.Path(location).resolve()
|
|
219
|
-
|
|
220
|
-
_LOGGER.debug(f"yaml_location in Settings.load(location={location}) is {yaml_location}")
|
|
297
|
+
# Load all detected YAML documents, these are considered global settings
|
|
221
298
|
|
|
222
|
-
|
|
299
|
+
yaml_locations = get_settings_locations(location, filename)
|
|
300
|
+
global_settings = load_global_settings(yaml_locations, filename, force)
|
|
223
301
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
str(yaml_location / filename), force=force
|
|
227
|
-
)
|
|
228
|
-
except FileNotFoundError as exc:
|
|
229
|
-
raise SettingsError(
|
|
230
|
-
f"Filename {filename} not found at location {yaml_location}."
|
|
231
|
-
) from exc
|
|
232
|
-
|
|
233
|
-
# Check if there were any groups defined in the YAML document
|
|
234
|
-
|
|
235
|
-
if not yaml_document_global:
|
|
236
|
-
raise SettingsError(f"Empty YAML document {filename} at {yaml_location}.")
|
|
302
|
+
if not global_settings:
|
|
303
|
+
raise SettingsError(f"There were no global settings defined for {filename} at {yaml_locations}.")
|
|
237
304
|
|
|
238
305
|
# Load the LOCAL settings YAML file
|
|
239
306
|
|
|
240
|
-
local_settings = {}
|
|
241
|
-
|
|
242
307
|
if add_local_settings:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
_LOGGER.debug(f"Using {local_settings_location} to update global settings.")
|
|
247
|
-
try:
|
|
248
|
-
yaml_document_local = cls.read_configuration_file(
|
|
249
|
-
local_settings_location, force=force
|
|
250
|
-
)
|
|
251
|
-
if yaml_document_local is None:
|
|
252
|
-
raise FileIsEmptyError()
|
|
253
|
-
local_settings = AttributeDict(
|
|
254
|
-
{name: value for name, value in yaml_document_local.items()}
|
|
255
|
-
)
|
|
256
|
-
except FileNotFoundError as exc:
|
|
257
|
-
raise SettingsError(
|
|
258
|
-
f"Local settings YAML file '{local_settings_location}' not found. "
|
|
259
|
-
f"Check your environment variable {get_local_settings_env_name()}."
|
|
260
|
-
) from exc
|
|
261
|
-
except FileIsEmptyError:
|
|
262
|
-
_LOGGER.warning(f"Local settings YAML file '{local_settings_location}' is empty. "
|
|
263
|
-
f"No local settings were loaded.")
|
|
264
|
-
except KeyError:
|
|
265
|
-
_LOGGER.debug(f"The environment variable {get_local_settings_env_name()} is not defined.")
|
|
308
|
+
local_settings = load_local_settings(force)
|
|
309
|
+
else:
|
|
310
|
+
local_settings = {}
|
|
266
311
|
|
|
267
312
|
if group_name in (None, ""):
|
|
268
|
-
global_settings =
|
|
269
|
-
{name: value for name, value in
|
|
313
|
+
global_settings = attrdict(
|
|
314
|
+
{name: value for name, value in global_settings.items()},
|
|
315
|
+
label="Settings"
|
|
270
316
|
)
|
|
271
317
|
if add_local_settings:
|
|
272
318
|
recursive_dict_update(global_settings, local_settings)
|
|
273
319
|
return global_settings
|
|
274
320
|
|
|
275
|
-
|
|
321
|
+
if group_name in global_settings:
|
|
322
|
+
include_global_settings = True
|
|
323
|
+
else:
|
|
324
|
+
include_global_settings = False
|
|
325
|
+
if group_name in local_settings:
|
|
326
|
+
include_local_settings = True
|
|
327
|
+
else:
|
|
328
|
+
include_local_settings = False
|
|
276
329
|
|
|
277
|
-
if
|
|
330
|
+
if not include_global_settings and not include_local_settings:
|
|
278
331
|
raise SettingsError(
|
|
279
|
-
f"Group name '{group_name}' is not defined in the
|
|
280
|
-
f"document '{filename}' at '{yaml_location}."
|
|
332
|
+
f"Group name '{group_name}' is not defined in the global nor in the local settings."
|
|
281
333
|
)
|
|
282
334
|
|
|
283
335
|
# Check if the group has any settings
|
|
284
336
|
|
|
285
|
-
if not
|
|
286
|
-
|
|
337
|
+
if include_global_settings and not global_settings[group_name]:
|
|
338
|
+
_LOGGER.warning(f"Empty group in YAML document {filename} for {group_name}.")
|
|
287
339
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
340
|
+
if include_global_settings:
|
|
341
|
+
group_settings = attrdict(
|
|
342
|
+
{name: value for name, value in global_settings[group_name].items()},
|
|
343
|
+
label=group_name
|
|
344
|
+
)
|
|
345
|
+
else:
|
|
346
|
+
group_settings = attrdict(label=group_name)
|
|
291
347
|
|
|
292
|
-
if add_local_settings and
|
|
348
|
+
if add_local_settings and include_local_settings:
|
|
293
349
|
recursive_dict_update(group_settings, local_settings[group_name])
|
|
294
350
|
|
|
295
351
|
return group_settings
|
|
@@ -312,34 +368,15 @@ class Settings:
|
|
|
312
368
|
trunc += " ..."
|
|
313
369
|
msg += f" {field}: {trunc}\n"
|
|
314
370
|
|
|
315
|
-
return msg
|
|
316
|
-
|
|
317
|
-
@classmethod
|
|
318
|
-
def set_profiling(cls, flag):
|
|
319
|
-
cls.__profile = flag
|
|
320
|
-
|
|
321
|
-
@classmethod
|
|
322
|
-
def profiling(cls):
|
|
323
|
-
return cls.__profile
|
|
371
|
+
return msg.rstrip()
|
|
324
372
|
|
|
325
|
-
@classmethod
|
|
326
|
-
def set_simulation_mode(cls, flag: bool):
|
|
327
|
-
cls.__simulation = flag
|
|
328
|
-
|
|
329
|
-
@classmethod
|
|
330
|
-
def simulation_mode(cls) -> bool:
|
|
331
|
-
return cls.__simulation
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
ignore_m_warning('egse.settings')
|
|
335
|
-
|
|
336
|
-
if __name__ == "__main__":
|
|
337
373
|
|
|
374
|
+
def main(args: list | None = None): # pragma: no cover
|
|
338
375
|
# We provide convenience to inspect the settings by calling this module directly from Python.
|
|
339
376
|
#
|
|
340
377
|
# python -m egse.settings
|
|
341
378
|
#
|
|
342
|
-
# Use the '--help' option to see
|
|
379
|
+
# Use the '--help' option to see what your choices are.
|
|
343
380
|
|
|
344
381
|
logging.basicConfig(level=20)
|
|
345
382
|
|
|
@@ -352,7 +389,10 @@ if __name__ == "__main__":
|
|
|
352
389
|
),
|
|
353
390
|
)
|
|
354
391
|
parser.add_argument("--local", action="store_true", help="print only the local settings.")
|
|
355
|
-
|
|
392
|
+
parser.add_argument("--global", action="store_true",
|
|
393
|
+
help="print only the global settings, don't include local settings.")
|
|
394
|
+
parser.add_argument("--group", help="print only settings for this group")
|
|
395
|
+
args = parser.parse_args(args or [])
|
|
356
396
|
|
|
357
397
|
# The following import will activate the pretty printing of the AttributeDict
|
|
358
398
|
# through the __rich__ method.
|
|
@@ -362,13 +402,20 @@ if __name__ == "__main__":
|
|
|
362
402
|
if args.local:
|
|
363
403
|
location = get_local_settings()
|
|
364
404
|
if location:
|
|
405
|
+
location = str(Path(location).expanduser().resolve())
|
|
365
406
|
settings = Settings.load(filename=location)
|
|
366
407
|
print(settings)
|
|
367
408
|
print(f"Loaded from [purple]{location}.")
|
|
368
409
|
else:
|
|
369
410
|
print("[red]No local settings defined.")
|
|
370
411
|
else:
|
|
371
|
-
|
|
412
|
+
# if the global option is given we don't want to include local settings
|
|
413
|
+
add_local_settings = False if vars(args)["global"] else True
|
|
414
|
+
|
|
415
|
+
if args.group:
|
|
416
|
+
settings = Settings.load(args.group, add_local_settings=add_local_settings)
|
|
417
|
+
else:
|
|
418
|
+
settings = Settings.load(add_local_settings=add_local_settings)
|
|
372
419
|
print(settings)
|
|
373
420
|
print("[blue]Memoized locations:")
|
|
374
421
|
locations = Settings.get_memoized_locations()
|
|
@@ -379,3 +426,9 @@ def get_site_id() -> str:
|
|
|
379
426
|
|
|
380
427
|
site = Settings.load("SITE")
|
|
381
428
|
return site.ID
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
# ignore_m_warning('egse.settings')
|
|
432
|
+
|
|
433
|
+
if __name__ == "__main__":
|
|
434
|
+
main()
|
egse/setup.py
CHANGED
|
@@ -133,6 +133,8 @@ from rich.tree import Tree
|
|
|
133
133
|
from egse.env import get_conf_repo_location
|
|
134
134
|
from egse.env import get_conf_repo_location_env_name
|
|
135
135
|
from egse.env import get_data_storage_location
|
|
136
|
+
from egse.env import has_conf_repo_location
|
|
137
|
+
from egse.env import print_env
|
|
136
138
|
from egse.response import Failure
|
|
137
139
|
from egse.env import get_conf_data_location
|
|
138
140
|
from egse.system import format_datetime
|
|
@@ -227,7 +229,11 @@ def _load_yaml(resource_name: str):
|
|
|
227
229
|
|
|
228
230
|
|
|
229
231
|
def _load_pandas(resource_name: str, separator: str):
|
|
230
|
-
"""
|
|
232
|
+
"""
|
|
233
|
+
Find and return the content of the given file as a pandas DataFrame object.
|
|
234
|
+
|
|
235
|
+
The file is loaded relative from the location of the configuration data
|
|
236
|
+
as defined by `get_conf_data_location()`.
|
|
231
237
|
|
|
232
238
|
Args:
|
|
233
239
|
- resource_name: Filename, preceded by "pandas//".
|
|
@@ -237,7 +243,7 @@ def _load_pandas(resource_name: str, separator: str):
|
|
|
237
243
|
|
|
238
244
|
parts = resource_name[8:].rsplit("/", 1)
|
|
239
245
|
[in_dir, fn] = parts if len(parts) > 1 else [None, parts[0]]
|
|
240
|
-
conf_location =
|
|
246
|
+
conf_location = get_conf_data_location()
|
|
241
247
|
|
|
242
248
|
try:
|
|
243
249
|
pandas_file_location = Path(conf_location) / in_dir / fn
|
|
@@ -336,7 +342,7 @@ class NavigableDict(dict):
|
|
|
336
342
|
|
|
337
343
|
"""
|
|
338
344
|
|
|
339
|
-
def __init__(self, head: dict = None):
|
|
345
|
+
def __init__(self, head: dict = None, label: str = None):
|
|
340
346
|
"""
|
|
341
347
|
Args:
|
|
342
348
|
head (dict): the original dictionary
|
|
@@ -344,6 +350,7 @@ class NavigableDict(dict):
|
|
|
344
350
|
head = head or {}
|
|
345
351
|
super().__init__(head)
|
|
346
352
|
self.__dict__["_memoized"] = {}
|
|
353
|
+
self.__dict__["_label"] = label
|
|
347
354
|
|
|
348
355
|
# By agreement, we only want the keys to be set as attributes if all keys are strings.
|
|
349
356
|
# That way we enforce that always all keys are navigable, or none.
|
|
@@ -572,7 +579,7 @@ class NavigableDict(dict):
|
|
|
572
579
|
return msg
|
|
573
580
|
|
|
574
581
|
def __rich__(self) -> Tree:
|
|
575
|
-
tree = Tree("NavigableDict", guide_style="dim")
|
|
582
|
+
tree = Tree(self.__dict__["_label"] or "NavigableDict", guide_style="dim")
|
|
576
583
|
walk_dict_tree(self, tree, text_style="dark grey")
|
|
577
584
|
return tree
|
|
578
585
|
|
|
@@ -636,8 +643,8 @@ class Setup(NavigableDict):
|
|
|
636
643
|
"""The Setup class represents a version of the configuration of the test facility, the
|
|
637
644
|
test setup and the Camera Under Test (CUT)."""
|
|
638
645
|
|
|
639
|
-
def __init__(self, nav_dict: NavigableDict | dict = None):
|
|
640
|
-
super().__init__(nav_dict or {})
|
|
646
|
+
def __init__(self, nav_dict: NavigableDict | dict = None, label: str = None):
|
|
647
|
+
super().__init__(nav_dict or {}, label=label)
|
|
641
648
|
|
|
642
649
|
@staticmethod
|
|
643
650
|
def from_dict(my_dict):
|
|
@@ -651,7 +658,7 @@ class Setup(NavigableDict):
|
|
|
651
658
|
>>> assert setup["ID"] == setup.ID == "my-setup-001"
|
|
652
659
|
|
|
653
660
|
"""
|
|
654
|
-
return Setup(my_dict)
|
|
661
|
+
return Setup(my_dict, label="Setup")
|
|
655
662
|
|
|
656
663
|
@staticmethod
|
|
657
664
|
def from_yaml_string(yaml_content: str = None):
|
|
@@ -674,15 +681,16 @@ class Setup(NavigableDict):
|
|
|
674
681
|
if "Setup" in setup_dict:
|
|
675
682
|
setup_dict = setup_dict["Setup"]
|
|
676
683
|
|
|
677
|
-
return Setup(setup_dict)
|
|
684
|
+
return Setup(setup_dict, label="Setup")
|
|
678
685
|
|
|
679
686
|
@staticmethod
|
|
680
687
|
@lru_cache
|
|
681
|
-
def from_yaml_file(filename: Union[str, Path] = None):
|
|
688
|
+
def from_yaml_file(filename: Union[str, Path] = None, add_local_settings: bool = True):
|
|
682
689
|
"""Loads a Setup from the given YAML file.
|
|
683
690
|
|
|
684
691
|
Args:
|
|
685
692
|
filename (str): the path of the YAML file to be loaded
|
|
693
|
+
add_local_settings (bool): if local settings shall be loaded and override the settings from the YAML file.
|
|
686
694
|
|
|
687
695
|
Returns:
|
|
688
696
|
a Setup that was loaded from the given location.
|
|
@@ -692,9 +700,11 @@ class Setup(NavigableDict):
|
|
|
692
700
|
if not filename:
|
|
693
701
|
raise ValueError("Invalid argument to function: No filename or None given.")
|
|
694
702
|
|
|
695
|
-
|
|
703
|
+
# MODULE_LOGGER.info(f"Loading {filename}...")
|
|
696
704
|
|
|
697
|
-
|
|
705
|
+
setup_dict = Settings.load("Setup", filename=filename, force=True, add_local_settings=add_local_settings)
|
|
706
|
+
|
|
707
|
+
setup = Setup(setup_dict, label="Setup")
|
|
698
708
|
setup.set_private_attribute("_filename", filename)
|
|
699
709
|
if setup_id := _parse_filename_for_setup_id(str(filename)):
|
|
700
710
|
setup.set_private_attribute("_setup_id", setup_id)
|
|
@@ -911,18 +921,15 @@ def get_setup(setup_id: int = None):
|
|
|
911
921
|
|
|
912
922
|
def _check_conditions_for_get_path_of_setup_file(site_id: str) -> Path:
|
|
913
923
|
"""
|
|
914
|
-
Check some pre-conditions that need to be met before we try to determine the
|
|
915
|
-
the requested Setup file.
|
|
924
|
+
Check some pre-conditions that need to be met before we try to determine the
|
|
925
|
+
file path for the requested Setup file.
|
|
916
926
|
|
|
917
927
|
The following checks are performed:
|
|
918
928
|
|
|
919
|
-
* if the environment variable '
|
|
920
|
-
|
|
929
|
+
* if the environment variable '{PROJECT}_CONF_REPO_LOCATION' is set
|
|
921
930
|
* if the directory specified in the env variable actually exists
|
|
922
|
-
|
|
923
931
|
* if the folder with the Setups exists for the given site_id
|
|
924
932
|
|
|
925
|
-
|
|
926
933
|
Args:
|
|
927
934
|
site_id (str): the name of the test house
|
|
928
935
|
|
|
@@ -939,9 +946,12 @@ def _check_conditions_for_get_path_of_setup_file(site_id: str) -> Path:
|
|
|
939
946
|
|
|
940
947
|
if not (repo_location := get_conf_repo_location()):
|
|
941
948
|
raise LookupError(
|
|
942
|
-
f"Environment variable doesn't exist
|
|
949
|
+
f"Environment variable doesn't exist or points to an invalid location, please (re-)define"
|
|
950
|
+
f" {repo_location_env} and try again."
|
|
943
951
|
)
|
|
944
952
|
|
|
953
|
+
print_env()
|
|
954
|
+
|
|
945
955
|
repo_location = Path(repo_location)
|
|
946
956
|
setup_location = repo_location / 'data' / site_id / 'conf'
|
|
947
957
|
|
|
@@ -962,11 +972,13 @@ def _check_conditions_for_get_path_of_setup_file(site_id: str) -> Path:
|
|
|
962
972
|
|
|
963
973
|
def get_path_of_setup_file(setup_id: int, site_id: str) -> Path:
|
|
964
974
|
"""
|
|
965
|
-
Returns the Path to the last Setup file for the given site_id. The last Setup
|
|
966
|
-
with the largest setup_id number.
|
|
975
|
+
Returns the Path to the last Setup file for the given site_id. The last Setup
|
|
976
|
+
file is the file with the largest setup_id number.
|
|
967
977
|
|
|
968
|
-
This function needs the environment variable <PROJECT>_CONF_REPO_LOCATION to
|
|
969
|
-
location of the repository with configuration data on your
|
|
978
|
+
This function needs the environment variable <PROJECT>_CONF_REPO_LOCATION to
|
|
979
|
+
be defined as the location of the repository with configuration data on your
|
|
980
|
+
disk. If the repo is not defined, the configuration data location will be used
|
|
981
|
+
instead.
|
|
970
982
|
|
|
971
983
|
Args:
|
|
972
984
|
setup_id (int): the identifier for the requested Setup
|
|
@@ -984,7 +996,10 @@ def get_path_of_setup_file(setup_id: int, site_id: str) -> Path:
|
|
|
984
996
|
|
|
985
997
|
"""
|
|
986
998
|
|
|
987
|
-
|
|
999
|
+
if not has_conf_repo_location():
|
|
1000
|
+
setup_location = Path(get_conf_data_location(site_id))
|
|
1001
|
+
else:
|
|
1002
|
+
setup_location = _check_conditions_for_get_path_of_setup_file(site_id)
|
|
988
1003
|
|
|
989
1004
|
if setup_id:
|
|
990
1005
|
files = list(setup_location.glob(f'SETUP_{site_id}_{setup_id:05d}_*.yaml'))
|
egse/system.py
CHANGED
|
@@ -28,6 +28,7 @@ import socket
|
|
|
28
28
|
import subprocess # For executing a shell command
|
|
29
29
|
import sys
|
|
30
30
|
import time
|
|
31
|
+
import warnings
|
|
31
32
|
from collections import namedtuple
|
|
32
33
|
from contextlib import contextmanager
|
|
33
34
|
from pathlib import Path
|
|
@@ -512,6 +513,9 @@ class AttributeDict(dict):
|
|
|
512
513
|
return self.__class__.__name__ + f"({{{sub_msg}{', ...' if len(self) > count else ''}}})"
|
|
513
514
|
|
|
514
515
|
|
|
516
|
+
attrdict = AttributeDict
|
|
517
|
+
|
|
518
|
+
|
|
515
519
|
def walk_dict_tree(dictionary: dict, tree: Tree, text_style: str = "green"):
|
|
516
520
|
for k, v in dictionary.items():
|
|
517
521
|
if isinstance(v, dict):
|
|
@@ -931,22 +935,23 @@ def env_var(**kwargs):
|
|
|
931
935
|
|
|
932
936
|
"""
|
|
933
937
|
saved_env = {}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
938
|
+
|
|
939
|
+
for k, v in kwargs.items():
|
|
940
|
+
saved_env[k] = os.environ.get(k)
|
|
941
|
+
if v is None:
|
|
942
|
+
if k in os.environ:
|
|
943
|
+
del os.environ[k]
|
|
944
|
+
else:
|
|
945
|
+
os.environ[k] = v
|
|
946
|
+
|
|
947
|
+
yield
|
|
948
|
+
|
|
949
|
+
for k, v in saved_env.items():
|
|
950
|
+
if v is None:
|
|
951
|
+
if k in os.environ:
|
|
952
|
+
del os.environ[k]
|
|
953
|
+
else:
|
|
954
|
+
os.environ[k] = v
|
|
950
955
|
|
|
951
956
|
|
|
952
957
|
def filter_by_attr(elements: Iterable, **attrs) -> List:
|
|
@@ -1120,7 +1125,7 @@ def is_namespace(module) -> bool:
|
|
|
1120
1125
|
return False
|
|
1121
1126
|
|
|
1122
1127
|
|
|
1123
|
-
def get_package_location(module) -> List[Path]:
|
|
1128
|
+
def get_package_location(module: str) -> List[Path]:
|
|
1124
1129
|
"""
|
|
1125
1130
|
Retrieves the file system locations associated with a Python package.
|
|
1126
1131
|
|
|
@@ -1153,6 +1158,7 @@ def get_package_location(module) -> List[Path]:
|
|
|
1153
1158
|
try:
|
|
1154
1159
|
module = importlib.import_module(module)
|
|
1155
1160
|
except TypeError:
|
|
1161
|
+
warnings.warn(f"The module is not found or is not valid: {module_name}.")
|
|
1156
1162
|
return []
|
|
1157
1163
|
|
|
1158
1164
|
if is_namespace(module):
|
|
File without changes
|
|
File without changes
|