cgse-common 2025.0.4__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.4.dist-info → cgse_common-2025.0.5.dist-info}/METADATA +1 -1
- {cgse_common-2025.0.4.dist-info → cgse_common-2025.0.5.dist-info}/RECORD +12 -12
- egse/control.py +0 -1
- egse/env.py +204 -30
- egse/protocol.py +1 -1
- egse/response.py +2 -1
- egse/services.py +4 -1
- egse/settings.py +182 -129
- egse/setup.py +37 -22
- egse/system.py +23 -17
- {cgse_common-2025.0.4.dist-info → cgse_common-2025.0.5.dist-info}/WHEEL +0 -0
- {cgse_common-2025.0.4.dist-info → cgse_common-2025.0.5.dist-info}/entry_points.txt +0 -0
|
@@ -2,10 +2,10 @@ egse/bits.py,sha256=1DPcC5R-8w-KLfbr2WLx-4Ezfwm6jpRMz-E5QfQ_YKw,11138
|
|
|
2
2
|
egse/calibration.py,sha256=DZ-LgEwamTWDukMqTr2JLCPt3M1lw6W0SfpHQfG7cXg,8989
|
|
3
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
10
|
egse/hk.py,sha256=5WMOGlx043XmEPj7ML1TkDzA8ZaTYLD-i5OcNUxE268,31163
|
|
11
11
|
egse/metrics.py,sha256=cZKMEe3OTT2uomj7vXjEl54JD0CStfEC4nCgS6U5YSM,3794
|
|
@@ -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/control.py
CHANGED
egse/env.py
CHANGED
|
@@ -40,32 +40,38 @@ WARNING:
|
|
|
40
40
|
from __future__ import annotations
|
|
41
41
|
|
|
42
42
|
__all__ = [
|
|
43
|
-
"
|
|
44
|
-
"get_site_id",
|
|
45
|
-
"get_data_storage_location",
|
|
46
|
-
"set_data_storage_location",
|
|
47
|
-
"get_data_storage_location_env_name",
|
|
43
|
+
"env_var",
|
|
48
44
|
"get_conf_data_location",
|
|
49
|
-
"set_conf_data_location",
|
|
50
45
|
"get_conf_data_location_env_name",
|
|
51
46
|
"get_conf_repo_location",
|
|
52
|
-
"set_conf_repo_location",
|
|
53
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",
|
|
54
52
|
"get_log_file_location",
|
|
55
|
-
"set_log_file_location",
|
|
56
53
|
"get_log_file_location_env_name",
|
|
57
|
-
"
|
|
54
|
+
"get_project_name",
|
|
55
|
+
"get_site_id",
|
|
56
|
+
"set_conf_data_location",
|
|
57
|
+
"set_conf_repo_location",
|
|
58
|
+
"set_data_storage_location",
|
|
58
59
|
"set_local_settings",
|
|
59
|
-
"
|
|
60
|
+
"set_log_file_location",
|
|
60
61
|
]
|
|
61
62
|
|
|
63
|
+
import contextlib
|
|
62
64
|
import os
|
|
63
65
|
import warnings
|
|
64
66
|
from pathlib import Path
|
|
65
67
|
|
|
66
68
|
from egse.system import all_logging_disabled
|
|
69
|
+
from egse.system import get_caller_info
|
|
67
70
|
from egse.system import ignore_m_warning
|
|
68
71
|
|
|
72
|
+
from rich.console import Console
|
|
73
|
+
console = Console(width=100)
|
|
74
|
+
|
|
69
75
|
# Every project shall have a PROJECT and a SITE_ID environment variable set. This variable will be used to
|
|
70
76
|
# create the other environment variables that are specific to the project.
|
|
71
77
|
|
|
@@ -123,11 +129,18 @@ class _Env:
|
|
|
123
129
|
self._env = {}
|
|
124
130
|
|
|
125
131
|
def set(self, key, value):
|
|
126
|
-
|
|
132
|
+
if value is None:
|
|
133
|
+
if key in self._env:
|
|
134
|
+
del self._env[key]
|
|
135
|
+
else:
|
|
136
|
+
self._env[key] = value
|
|
127
137
|
|
|
128
138
|
def get(self, key) -> str:
|
|
129
139
|
return self._env.get(key, NoValue())
|
|
130
140
|
|
|
141
|
+
def __rich__(self):
|
|
142
|
+
return self._env
|
|
143
|
+
|
|
131
144
|
|
|
132
145
|
_env = _Env()
|
|
133
146
|
|
|
@@ -159,28 +172,60 @@ def _check_no_value(var_name, value):
|
|
|
159
172
|
"""Raise a ValueError when the value for the variable is NoValue."""
|
|
160
173
|
if value == NoValue():
|
|
161
174
|
project = _env.get("PROJECT")
|
|
162
|
-
env_name = var_name if var_name
|
|
175
|
+
env_name = var_name if var_name in ("PROJECT", "SITE_ID") else f"{project}_{var_name}"
|
|
163
176
|
raise ValueError(
|
|
164
177
|
f"The environment variable {env_name} is not set. "
|
|
165
178
|
f"Please set the environment variable before proceeding."
|
|
166
179
|
)
|
|
167
180
|
|
|
168
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
|
+
|
|
169
200
|
def get_project_name():
|
|
201
|
+
"""Get the PROJECT name. Return None when the PROJECT is not set."""
|
|
170
202
|
return _env.get("PROJECT") or None
|
|
171
203
|
|
|
172
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
|
+
|
|
173
211
|
def get_site_id():
|
|
212
|
+
"""Get the SITE_ID. Return None if the SITE_ID is not set."""
|
|
174
213
|
return _env.get("SITE_ID") or None
|
|
175
214
|
|
|
176
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
|
+
|
|
177
222
|
def get_data_storage_location_env_name() -> str:
|
|
178
223
|
"""Returns the name of the environment variable for the project."""
|
|
179
224
|
project = _env.get("PROJECT")
|
|
180
225
|
return f"{project}_DATA_STORAGE_LOCATION"
|
|
181
226
|
|
|
182
227
|
|
|
183
|
-
def set_data_storage_location(location: str | Path):
|
|
228
|
+
def set_data_storage_location(location: str | Path | None):
|
|
184
229
|
"""
|
|
185
230
|
Sets the environment variable and the internal representation to the given value.
|
|
186
231
|
|
|
@@ -189,14 +234,19 @@ def set_data_storage_location(location: str | Path):
|
|
|
189
234
|
"""
|
|
190
235
|
env_name = get_data_storage_location_env_name()
|
|
191
236
|
|
|
192
|
-
|
|
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
|
+
|
|
193
243
|
if not Path(location).exists():
|
|
194
244
|
warnings.warn(
|
|
195
245
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
196
246
|
)
|
|
197
247
|
|
|
198
|
-
os.environ[env_name] = location
|
|
199
|
-
_env.set('DATA_STORAGE_LOCATION', location)
|
|
248
|
+
os.environ[env_name] = str(location)
|
|
249
|
+
_env.set('DATA_STORAGE_LOCATION', str(location))
|
|
200
250
|
|
|
201
251
|
|
|
202
252
|
def get_data_storage_location(site_id: str = None) -> str:
|
|
@@ -223,6 +273,9 @@ def get_data_storage_location(site_id: str = None) -> str:
|
|
|
223
273
|
"""
|
|
224
274
|
global _env
|
|
225
275
|
|
|
276
|
+
project = _env.get("PROJECT")
|
|
277
|
+
_check_no_value("PROJECT", project)
|
|
278
|
+
|
|
226
279
|
site_id = site_id or _env.get("SITE_ID")
|
|
227
280
|
_check_no_value("SITE_ID", site_id)
|
|
228
281
|
|
|
@@ -240,7 +293,7 @@ def get_conf_data_location_env_name() -> str:
|
|
|
240
293
|
return f"{project}_CONF_DATA_LOCATION"
|
|
241
294
|
|
|
242
295
|
|
|
243
|
-
def set_conf_data_location(location: str | Path):
|
|
296
|
+
def set_conf_data_location(location: str | Path | None):
|
|
244
297
|
"""
|
|
245
298
|
Sets the environment variable and the internal representation to the given value.
|
|
246
299
|
|
|
@@ -250,7 +303,12 @@ def set_conf_data_location(location: str | Path):
|
|
|
250
303
|
|
|
251
304
|
env_name = get_conf_data_location_env_name()
|
|
252
305
|
|
|
253
|
-
|
|
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
|
+
|
|
254
312
|
if not Path(location).exists():
|
|
255
313
|
warnings.warn(
|
|
256
314
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
@@ -303,7 +361,7 @@ def get_log_file_location_env_name():
|
|
|
303
361
|
return f"{project}_LOG_FILE_LOCATION"
|
|
304
362
|
|
|
305
363
|
|
|
306
|
-
def set_log_file_location(location: str | Path):
|
|
364
|
+
def set_log_file_location(location: str | Path | None):
|
|
307
365
|
"""
|
|
308
366
|
Sets the environment variable and the internal representation to the given value.
|
|
309
367
|
|
|
@@ -313,7 +371,12 @@ def set_log_file_location(location: str | Path):
|
|
|
313
371
|
|
|
314
372
|
env_name = get_log_file_location_env_name()
|
|
315
373
|
|
|
316
|
-
|
|
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
|
+
|
|
317
380
|
if not Path(location).exists():
|
|
318
381
|
warnings.warn(
|
|
319
382
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
@@ -365,17 +428,24 @@ def get_local_settings_env_name() -> str:
|
|
|
365
428
|
return f"{project}_LOCAL_SETTINGS"
|
|
366
429
|
|
|
367
430
|
|
|
368
|
-
def set_local_settings(path: str | Path):
|
|
431
|
+
def set_local_settings(path: str | Path | None):
|
|
369
432
|
"""
|
|
370
433
|
Sets the environment variable and the internal representation to the given value.
|
|
371
434
|
|
|
435
|
+
When the path is set to None, the environment variable will be unset.
|
|
436
|
+
|
|
372
437
|
Warnings:
|
|
373
438
|
Issues a warning when the given path doesn't exist.
|
|
374
439
|
"""
|
|
375
440
|
|
|
376
441
|
env_name = get_local_settings_env_name()
|
|
377
442
|
|
|
378
|
-
|
|
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
|
+
|
|
379
449
|
if not Path(path).exists():
|
|
380
450
|
warnings.warn(
|
|
381
451
|
f"The location you provided for the environment variable {env_name} doesn't exist: {path}."
|
|
@@ -399,34 +469,61 @@ def get_local_settings() -> str:
|
|
|
399
469
|
return local_settings or None
|
|
400
470
|
|
|
401
471
|
|
|
472
|
+
def has_conf_repo_location() -> bool:
|
|
473
|
+
location = _env.get("CONF_REPO_LOCATION")
|
|
474
|
+
return True if location else False
|
|
475
|
+
|
|
476
|
+
|
|
402
477
|
def get_conf_repo_location_env_name() -> str:
|
|
403
478
|
"""Returns the name of the environment variable for the project."""
|
|
404
479
|
project = _env.get("PROJECT")
|
|
405
480
|
return f"{project}_CONF_REPO_LOCATION"
|
|
406
481
|
|
|
407
482
|
|
|
408
|
-
def get_conf_repo_location() -> str:
|
|
409
|
-
"""
|
|
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
|
+
"""
|
|
410
491
|
|
|
411
492
|
location = _env.get("CONF_REPO_LOCATION")
|
|
412
493
|
|
|
413
|
-
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():
|
|
414
502
|
warnings.warn(f"The location of the configuration data repository doesn't exist: {location}.")
|
|
503
|
+
return None
|
|
415
504
|
|
|
416
|
-
return location
|
|
505
|
+
return location
|
|
417
506
|
|
|
418
507
|
|
|
419
|
-
def set_conf_repo_location(location: str):
|
|
508
|
+
def set_conf_repo_location(location: str | Path | None):
|
|
420
509
|
"""
|
|
421
510
|
Sets the environment variable and the internal representation to the given value.
|
|
422
511
|
|
|
512
|
+
When the location is None, the environment variable will be unset and its internal
|
|
513
|
+
representation will be NoValue().
|
|
514
|
+
|
|
423
515
|
Warnings:
|
|
424
516
|
Issues a warning when the given location doesn't exist.
|
|
425
517
|
"""
|
|
426
518
|
|
|
427
519
|
env_name = get_conf_repo_location_env_name()
|
|
428
520
|
|
|
429
|
-
|
|
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
|
+
|
|
430
527
|
if not Path(location).exists():
|
|
431
528
|
warnings.warn(
|
|
432
529
|
f"The location you provided for the environment variable {env_name} doesn't exist: {location}."
|
|
@@ -436,7 +533,69 @@ def set_conf_repo_location(location: str):
|
|
|
436
533
|
_env.set('CONF_REPO_LOCATION', location)
|
|
437
534
|
|
|
438
535
|
|
|
439
|
-
|
|
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()
|
|
440
599
|
|
|
441
600
|
|
|
442
601
|
def main(args: list | None = None): # pragma: no cover
|
|
@@ -517,8 +676,10 @@ def main(args: list | None = None): # pragma: no cover
|
|
|
517
676
|
location = get_data_storage_location()
|
|
518
677
|
if not Path(location).exists():
|
|
519
678
|
if args.mkdir:
|
|
520
|
-
rich.print(f" [green]⟶ Creating data storage location: {location}[/]")
|
|
679
|
+
rich.print(f" [green]⟶ Creating data storage location: {location} (+ daily + obs)[/]")
|
|
521
680
|
Path(location).mkdir(parents=True)
|
|
681
|
+
(Path(location) / "daily").mkdir()
|
|
682
|
+
(Path(location) / "obs").mkdir()
|
|
522
683
|
else:
|
|
523
684
|
rich.print(" [red]⟶ ERROR: The data storage location doesn't exist![/]")
|
|
524
685
|
else:
|
|
@@ -540,6 +701,16 @@ def main(args: list | None = None): # pragma: no cover
|
|
|
540
701
|
except ValueError as exc:
|
|
541
702
|
rich.print(f" get_conf_data_location() = [red]{exc}[/]")
|
|
542
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
|
+
|
|
543
714
|
try:
|
|
544
715
|
rich.print(f" {get_log_file_location() = }", flush=True, end="")
|
|
545
716
|
location = get_log_file_location()
|
|
@@ -623,6 +794,9 @@ def main(args: list | None = None): # pragma: no cover
|
|
|
623
794
|
# PLATO_COMMON_EGSE_PATH - YES
|
|
624
795
|
|
|
625
796
|
|
|
797
|
+
ignore_m_warning('egse.env')
|
|
798
|
+
|
|
799
|
+
|
|
626
800
|
if __name__ == "__main__":
|
|
627
801
|
import sys
|
|
628
802
|
main(sys.argv[1:])
|
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//".
|
|
@@ -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
|