cgse-common 2025.0.4__py3-none-any.whl → 2025.0.6__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.6.dist-info}/METADATA +1 -1
- {cgse_common-2025.0.4.dist-info → cgse_common-2025.0.6.dist-info}/RECORD +15 -15
- cgse_common-2025.0.6.dist-info/entry_points.txt +5 -0
- egse/control.py +0 -1
- egse/decorators.py +13 -4
- egse/env.py +223 -37
- egse/plugin.py +68 -6
- egse/protocol.py +1 -1
- egse/response.py +2 -1
- egse/services.py +4 -1
- egse/settings.py +260 -193
- egse/settings.yaml +4 -6
- egse/setup.py +49 -26
- egse/system.py +23 -17
- cgse_common-2025.0.4.dist-info/entry_points.txt +0 -2
- {cgse_common-2025.0.4.dist-info → cgse_common-2025.0.6.dist-info}/WHEEL +0 -0
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
|
@@ -3,12 +3,19 @@ The Settings class handles user and configuration settings that are provided in
|
|
|
3
3
|
a [`YAML`](http://yaml.org) file.
|
|
4
4
|
|
|
5
5
|
The idea is that settings are grouped by components or any arbitrary grouping that makes sense for
|
|
6
|
-
the application or for the user.
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
the application or for the user. Settings are also modular and provided by each package by means
|
|
7
|
+
of entry-points.The Settings class can read from different YAML files.
|
|
8
|
+
|
|
9
|
+
By default, settings are loaded from a file called `settings.yaml`, but this can be changed in the entry-point
|
|
10
|
+
definition.
|
|
11
|
+
|
|
12
|
+
The yaml configuration files are provided as entry-points by the packages that provided an entry-point
|
|
13
|
+
group 'cgse.settings'. The Settings dictionary (attrdict) is constructed from the configuration YAML
|
|
14
|
+
files from each of the packages. Settings can be overwritten by the next package configuration file.
|
|
15
|
+
So, make sure the group names in each package configuration file are unique.
|
|
9
16
|
|
|
10
17
|
The YAML file is read and the configuration parameters for the given group are
|
|
11
|
-
|
|
18
|
+
available as instance variables of the returned class.
|
|
12
19
|
|
|
13
20
|
The intended use is as follows:
|
|
14
21
|
|
|
@@ -23,11 +30,10 @@ The intended use is as follows:
|
|
|
23
30
|
else:
|
|
24
31
|
raise RMAPError("Attempt to access outside the RMAP memory map.")
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
The above code reads the settings from the default YAML file for a group called ``DSI``.
|
|
33
|
+
The above code reads the settings from the default YAML file for a group called `DSI`.
|
|
28
34
|
The settings will then be available as variables of the returned class, in this case
|
|
29
|
-
|
|
30
|
-
if a configuration parameter is defined like this:
|
|
35
|
+
`dsi_settings`. The returned class is and behaves also like a dictionary, so you can
|
|
36
|
+
check if a configuration parameter is defined like this:
|
|
31
37
|
|
|
32
38
|
if "DSI_FEE_IP_ADDRESS" not in dsi_settings:
|
|
33
39
|
# define the IP address of the DSI
|
|
@@ -46,53 +52,37 @@ The YAML section for the above code looks like this:
|
|
|
46
52
|
RMAP_BASE_ADDRESS: 0x00000000 # The start of the RMAP memory map managed by the FEE
|
|
47
53
|
RMAP_MEMORY_SIZE: 4096 # The size of the RMAP memory map managed by the FEE
|
|
48
54
|
|
|
49
|
-
When you want to read settings from another YAML file, specify the
|
|
50
|
-
If that file is located at a specific location, also use the
|
|
55
|
+
When you want to read settings from another YAML file, specify the `filename=` keyword.
|
|
56
|
+
If that file is located at a specific location, also use the `location=` keyword.
|
|
51
57
|
|
|
52
58
|
my_settings = Settings.load(filename="user.yaml", location="/Users/JohnDoe")
|
|
53
59
|
|
|
54
|
-
The above code will read the
|
|
60
|
+
The above code will read the YAML file from the given location and not from the entry-points.
|
|
55
61
|
|
|
56
62
|
"""
|
|
63
|
+
from __future__ import annotations
|
|
57
64
|
|
|
58
65
|
import logging
|
|
59
|
-
import os
|
|
60
|
-
import pathlib
|
|
61
66
|
import re
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
from typing import Any
|
|
62
69
|
|
|
63
70
|
import yaml # This module is provided by the pip package PyYaml - pip install pyyaml
|
|
64
71
|
|
|
65
|
-
from egse.env import get_local_settings
|
|
66
72
|
from egse.env import get_local_settings_env_name
|
|
67
|
-
from egse.
|
|
68
|
-
from egse.system import
|
|
69
|
-
from egse.system import get_caller_info
|
|
70
|
-
from egse.system import ignore_m_warning
|
|
73
|
+
from egse.env import get_local_settings_path
|
|
74
|
+
from egse.system import attrdict
|
|
71
75
|
from egse.system import recursive_dict_update
|
|
72
76
|
|
|
73
77
|
_LOGGER = logging.getLogger(__name__)
|
|
74
78
|
|
|
79
|
+
_HERE = Path(__file__).resolve().parent
|
|
80
|
+
|
|
75
81
|
|
|
76
82
|
class SettingsError(Exception):
|
|
77
83
|
pass
|
|
78
84
|
|
|
79
85
|
|
|
80
|
-
def is_defined(cls, name):
|
|
81
|
-
return hasattr(cls, name)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def get_attr_value(cls, name, default=None):
|
|
85
|
-
try:
|
|
86
|
-
return getattr(cls, name)
|
|
87
|
-
except AttributeError:
|
|
88
|
-
return default
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def set_attr_value(cls, name, value):
|
|
92
|
-
if hasattr(cls, name):
|
|
93
|
-
raise KeyError(f"Overwriting setting {name} with {value}, was {hasattr(cls, name)}")
|
|
94
|
-
|
|
95
|
-
|
|
96
86
|
# Fix the problem: YAML loads 5e-6 as string and not a number
|
|
97
87
|
# https://stackoverflow.com/questions/30458977/yaml-loads-5e-6-as-string-and-not-a-number
|
|
98
88
|
|
|
@@ -109,6 +99,133 @@ SAFE_LOADER.add_implicit_resolver(
|
|
|
109
99
|
list(u'-+0123456789.'))
|
|
110
100
|
|
|
111
101
|
|
|
102
|
+
def load_settings_file(path: Path, filename: str, force: bool = False) -> attrdict:
|
|
103
|
+
"""
|
|
104
|
+
Loads the YAML configuration file that is located at `path / filename`.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
- path (PATH): the folder where the YAML file is located
|
|
108
|
+
- filename (str): the name of the YAML configuration file
|
|
109
|
+
- force (bool): force reloading, i.e. don't use the cached information
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
A SettingsError when the configuration file doesn't exist or cannot be found.
|
|
113
|
+
|
|
114
|
+
A SettingsError when there was an error reading the configuration file.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
A dictionary (attrdict) with all the settings from the given file.
|
|
118
|
+
|
|
119
|
+
Note that, in case of an empty configuration file, and empty dictionary
|
|
120
|
+
is returned and a warning message is issued.
|
|
121
|
+
"""
|
|
122
|
+
try:
|
|
123
|
+
yaml_document = read_configuration_file(path / filename, force=force)
|
|
124
|
+
settings = attrdict(
|
|
125
|
+
{name: value for name, value in yaml_document.items()}
|
|
126
|
+
)
|
|
127
|
+
except FileNotFoundError as exc:
|
|
128
|
+
raise SettingsError(
|
|
129
|
+
f"The Settings YAML file '{filename}' is not found at {path!s}. "
|
|
130
|
+
) from exc
|
|
131
|
+
|
|
132
|
+
if not settings:
|
|
133
|
+
_LOGGER.warning(
|
|
134
|
+
f"The Settings YAML file '{filename}' at {path!s} is empty. "
|
|
135
|
+
f"No local settings were loaded, an empty dictionary is returned.")
|
|
136
|
+
|
|
137
|
+
return settings
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def load_global_settings(entry_point: str = 'cgse.settings', force: bool = False) -> attrdict:
|
|
141
|
+
"""
|
|
142
|
+
Loads the settings that are defined by the given entry_point. The entry-points are defined in the
|
|
143
|
+
`pyproject.toml` files of the packages that export their global settings.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
- entry_point (str): the name of the entry-point group [default: 'cgse.settings']
|
|
147
|
+
- force (bool): force reloading the settings, i.e. ignore the cache
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
A dictionary (attrdict) containing a collection of all the settings exported by the packages
|
|
151
|
+
through the given entry-point.
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
from egse.plugin import get_file_infos
|
|
155
|
+
|
|
156
|
+
ep_settings = get_file_infos(entry_point)
|
|
157
|
+
|
|
158
|
+
global_settings = attrdict(label="Settings")
|
|
159
|
+
|
|
160
|
+
for ep_name, (path, filename) in ep_settings.items():
|
|
161
|
+
settings = load_settings_file(path, filename, force)
|
|
162
|
+
recursive_dict_update(global_settings, settings)
|
|
163
|
+
|
|
164
|
+
return global_settings
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def load_local_settings(force: bool = False) -> attrdict:
|
|
168
|
+
"""
|
|
169
|
+
Loads the local settings file that is defined from the environment variable <PROJECT>_LOCAL_SETTINGS.
|
|
170
|
+
|
|
171
|
+
This function might return an empty dictionary when
|
|
172
|
+
|
|
173
|
+
- the local settings YAML file is empty
|
|
174
|
+
- the local settings environment variable is not defined.
|
|
175
|
+
|
|
176
|
+
in both cases a warning message is logged.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
A dictionary (attrdict) with all local settings.
|
|
180
|
+
|
|
181
|
+
Raises:
|
|
182
|
+
A SettingsError when the local settings YAML file is not found. Check the <PROJECT>_LOCAL_SETTINGS
|
|
183
|
+
environment variable.
|
|
184
|
+
"""
|
|
185
|
+
local_settings = attrdict()
|
|
186
|
+
|
|
187
|
+
local_settings_path = get_local_settings_path()
|
|
188
|
+
|
|
189
|
+
if local_settings_path:
|
|
190
|
+
path = Path(local_settings_path)
|
|
191
|
+
local_settings = load_settings_file(path.parent, path.name, force)
|
|
192
|
+
|
|
193
|
+
return local_settings
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def read_configuration_file(filename: Path, *, force=False):
|
|
197
|
+
"""
|
|
198
|
+
Read the YAML input configuration file. The configuration file is only read
|
|
199
|
+
once and memoized as load optimization.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
filename (Path): the fully qualified filename of the YAML file
|
|
203
|
+
force (bool): force reloading the file, even when it was memoized
|
|
204
|
+
|
|
205
|
+
Raises:
|
|
206
|
+
A SettingsError when there was an error reading the YAML file.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
a dictionary containing all the configuration settings from the YAML file.
|
|
210
|
+
"""
|
|
211
|
+
filename = str(filename)
|
|
212
|
+
|
|
213
|
+
if force or not Settings.is_memoized(filename):
|
|
214
|
+
|
|
215
|
+
_LOGGER.debug(f"Parsing YAML configuration file {filename}.")
|
|
216
|
+
|
|
217
|
+
with open(filename, "r") as stream:
|
|
218
|
+
try:
|
|
219
|
+
yaml_document = yaml.load(stream, Loader=SAFE_LOADER)
|
|
220
|
+
except yaml.YAMLError as exc:
|
|
221
|
+
_LOGGER.error(exc)
|
|
222
|
+
raise SettingsError(f"Error loading YAML document {filename}") from exc
|
|
223
|
+
|
|
224
|
+
Settings.add_memoized(filename, yaml_document)
|
|
225
|
+
|
|
226
|
+
return Settings.get_memoized(filename) or {}
|
|
227
|
+
|
|
228
|
+
|
|
112
229
|
class Settings:
|
|
113
230
|
"""
|
|
114
231
|
The Settings class provides a load() method that loads configuration settings for a group
|
|
@@ -116,9 +233,7 @@ class Settings:
|
|
|
116
233
|
"""
|
|
117
234
|
|
|
118
235
|
__memoized_yaml = {} # Memoized settings yaml files
|
|
119
|
-
|
|
120
236
|
__profile = False # Used for profiling methods and functions
|
|
121
|
-
__simulation = False # Use simulation mode where applicable and possible
|
|
122
237
|
|
|
123
238
|
LOG_FORMAT_DEFAULT = "%(levelname)s:%(module)s:%(lineno)d:%(message)s"
|
|
124
239
|
LOG_FORMAT_FULL = "%(asctime)23s:%(levelname)8s:%(lineno)5d:%(name)-20s: %(message)s"
|
|
@@ -132,167 +247,122 @@ class Settings:
|
|
|
132
247
|
LOG_FORMAT_DATE = "%d/%m/%Y %H:%M:%S"
|
|
133
248
|
|
|
134
249
|
@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
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
a dictionary containing all the configuration settings from the YAML file.
|
|
146
|
-
"""
|
|
147
|
-
if force or filename not in cls.__memoized_yaml:
|
|
148
|
-
|
|
149
|
-
_LOGGER.debug(f"Parsing YAML configuration file {filename}.")
|
|
150
|
-
|
|
151
|
-
with open(filename, "r") as stream:
|
|
152
|
-
try:
|
|
153
|
-
yaml_document = yaml.load(stream, Loader=SAFE_LOADER)
|
|
154
|
-
except yaml.YAMLError as exc:
|
|
155
|
-
_LOGGER.error(exc)
|
|
156
|
-
raise SettingsError(f"Error loading YAML document {filename}") from exc
|
|
157
|
-
|
|
158
|
-
cls.__memoized_yaml[filename] = yaml_document
|
|
159
|
-
|
|
160
|
-
return cls.__memoized_yaml[filename]
|
|
250
|
+
def get_memoized_locations(cls) -> list:
|
|
251
|
+
return list(cls.__memoized_yaml.keys())
|
|
161
252
|
|
|
162
253
|
@classmethod
|
|
163
|
-
def
|
|
164
|
-
return cls.__memoized_yaml
|
|
254
|
+
def is_memoized(cls, filename: str) -> bool:
|
|
255
|
+
return filename in cls.__memoized_yaml
|
|
165
256
|
|
|
166
257
|
@classmethod
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
"""
|
|
170
|
-
Load the settings for the given group from YAML configuration file.
|
|
171
|
-
When no group is provided, the complete configuration is returned.
|
|
258
|
+
def add_memoized(cls, filename: str, yaml_document: Any):
|
|
259
|
+
cls.__memoized_yaml[filename] = yaml_document
|
|
172
260
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
About the ``location`` keyword several options are available.
|
|
177
|
-
|
|
178
|
-
* when no location is given, i.e. ``location=None``, the YAML settings file is searched for
|
|
179
|
-
at the same location as the settings module.
|
|
180
|
-
|
|
181
|
-
* when a relative location is given, the YAML settings file is searched for relative to the
|
|
182
|
-
current working directory.
|
|
261
|
+
@classmethod
|
|
262
|
+
def get_memoized(cls, filename: str):
|
|
263
|
+
return cls.__memoized_yaml.get(filename)
|
|
183
264
|
|
|
184
|
-
|
|
265
|
+
@classmethod
|
|
266
|
+
def clear_memoized(cls):
|
|
267
|
+
cls.__memoized_yaml.clear()
|
|
185
268
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
location (str): the path to the location of the YAML file
|
|
190
|
-
force (bool): force reloading the file
|
|
191
|
-
add_local_settings (bool): update the Settings with site specific local settings
|
|
269
|
+
@classmethod
|
|
270
|
+
def set_profiling(cls, flag):
|
|
271
|
+
cls.__profile = flag
|
|
192
272
|
|
|
193
|
-
|
|
194
|
-
|
|
273
|
+
@classmethod
|
|
274
|
+
def profiling(cls):
|
|
275
|
+
return cls.__profile
|
|
195
276
|
|
|
196
|
-
|
|
197
|
-
|
|
277
|
+
@staticmethod
|
|
278
|
+
def _load_all(
|
|
279
|
+
entry_point: str = 'cgse.settings',
|
|
280
|
+
add_local_settings: bool = False,
|
|
281
|
+
force: bool = False
|
|
282
|
+
) -> attrdict:
|
|
198
283
|
"""
|
|
284
|
+
Loads all settings from all package with the entry point 'cgse.settings'
|
|
285
|
+
"""
|
|
286
|
+
global_settings = load_global_settings(entry_point, force)
|
|
199
287
|
|
|
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
|
|
288
|
+
# Load the LOCAL settings YAML file
|
|
209
289
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
yaml_location = _THIS_FILE_LOCATION
|
|
214
|
-
else:
|
|
290
|
+
if add_local_settings:
|
|
291
|
+
local_settings = load_local_settings(force)
|
|
292
|
+
recursive_dict_update(global_settings, local_settings)
|
|
215
293
|
|
|
216
|
-
|
|
294
|
+
return global_settings
|
|
217
295
|
|
|
218
|
-
|
|
296
|
+
@staticmethod
|
|
297
|
+
def _load_group(
|
|
298
|
+
group_name: str,
|
|
299
|
+
entry_point: str = 'cgse.settings',
|
|
300
|
+
add_local_settings: bool = False,
|
|
301
|
+
force: bool = False
|
|
302
|
+
) -> attrdict:
|
|
219
303
|
|
|
220
|
-
|
|
304
|
+
global_settings = load_global_settings(entry_point, force)
|
|
221
305
|
|
|
222
|
-
|
|
306
|
+
group_settings = attrdict(label=group_name)
|
|
223
307
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
308
|
+
if group_name in global_settings:
|
|
309
|
+
group_settings = attrdict(
|
|
310
|
+
{name: value for name, value in global_settings[group_name].items()},
|
|
311
|
+
label=group_name
|
|
227
312
|
)
|
|
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
313
|
|
|
235
|
-
if
|
|
236
|
-
|
|
314
|
+
if add_local_settings:
|
|
315
|
+
local_settings = load_local_settings(force)
|
|
316
|
+
if group_name in local_settings:
|
|
317
|
+
recursive_dict_update(group_settings, local_settings[group_name])
|
|
237
318
|
|
|
238
|
-
|
|
319
|
+
if not group_settings:
|
|
320
|
+
raise SettingsError(
|
|
321
|
+
f"Group name '{group_name}' is not defined in the global nor in the local settings."
|
|
322
|
+
)
|
|
239
323
|
|
|
240
|
-
|
|
324
|
+
return group_settings
|
|
241
325
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
local_settings_location = get_local_settings()
|
|
245
|
-
if local_settings_location:
|
|
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.")
|
|
266
|
-
|
|
267
|
-
if group_name in (None, ""):
|
|
268
|
-
global_settings = AttributeDict(
|
|
269
|
-
{name: value for name, value in yaml_document_global.items()}
|
|
270
|
-
)
|
|
271
|
-
if add_local_settings:
|
|
272
|
-
recursive_dict_update(global_settings, local_settings)
|
|
273
|
-
return global_settings
|
|
326
|
+
@staticmethod
|
|
327
|
+
def _load_one(location: str, filename: str, force=False) -> attrdict:
|
|
274
328
|
|
|
275
|
-
|
|
329
|
+
return load_settings_file(Path(location).expanduser(), filename, force)
|
|
276
330
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
331
|
+
@classmethod
|
|
332
|
+
def load(
|
|
333
|
+
cls,
|
|
334
|
+
group_name=None, filename="settings.yaml", location=None,
|
|
335
|
+
*, add_local_settings=True, force=False
|
|
336
|
+
):
|
|
337
|
+
"""
|
|
338
|
+
Load the settings for the given group. When no group is provided, the
|
|
339
|
+
complete configuration is returned.
|
|
282
340
|
|
|
283
|
-
|
|
341
|
+
The Settings are loaded from entry-points that are defined in each of the
|
|
342
|
+
packages that provide a Settings file.
|
|
284
343
|
|
|
285
|
-
|
|
286
|
-
|
|
344
|
+
If a location is explicitly provided, the Settings will be loaded from that
|
|
345
|
+
location, using the given filename or the default (which is settings.yaml).
|
|
287
346
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
347
|
+
Args:
|
|
348
|
+
group_name (str): the name of one of the main groups from the YAML file
|
|
349
|
+
filename (str): the name of the YAML file to read [default=settings.yaml]
|
|
350
|
+
location (str, Path): the path to the location of the YAML file
|
|
351
|
+
force (bool): force reloading the file
|
|
352
|
+
add_local_settings (bool): update the Settings with site specific local settings
|
|
291
353
|
|
|
292
|
-
|
|
293
|
-
|
|
354
|
+
Returns:
|
|
355
|
+
a dynamically created class with the configuration parameters as instance variables.
|
|
294
356
|
|
|
295
|
-
|
|
357
|
+
Raises:
|
|
358
|
+
a SettingsError when the group is not defined in the YAML file.
|
|
359
|
+
"""
|
|
360
|
+
if group_name:
|
|
361
|
+
return cls._load_group(group_name, add_local_settings=add_local_settings, force=force)
|
|
362
|
+
elif location:
|
|
363
|
+
return cls._load_one(location=location, filename=filename, force=force)
|
|
364
|
+
else:
|
|
365
|
+
return cls._load_all(add_local_settings=add_local_settings, force=force)
|
|
296
366
|
|
|
297
367
|
@classmethod
|
|
298
368
|
def to_string(cls):
|
|
@@ -312,34 +382,15 @@ class Settings:
|
|
|
312
382
|
trunc += " ..."
|
|
313
383
|
msg += f" {field}: {trunc}\n"
|
|
314
384
|
|
|
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
|
|
324
|
-
|
|
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')
|
|
385
|
+
return msg.rstrip()
|
|
335
386
|
|
|
336
|
-
if __name__ == "__main__":
|
|
337
387
|
|
|
388
|
+
def main(args: list | None = None): # pragma: no cover
|
|
338
389
|
# We provide convenience to inspect the settings by calling this module directly from Python.
|
|
339
390
|
#
|
|
340
391
|
# python -m egse.settings
|
|
341
392
|
#
|
|
342
|
-
# Use the '--help' option to see
|
|
393
|
+
# Use the '--help' option to see what your choices are.
|
|
343
394
|
|
|
344
395
|
logging.basicConfig(level=20)
|
|
345
396
|
|
|
@@ -352,7 +403,10 @@ if __name__ == "__main__":
|
|
|
352
403
|
),
|
|
353
404
|
)
|
|
354
405
|
parser.add_argument("--local", action="store_true", help="print only the local settings.")
|
|
355
|
-
|
|
406
|
+
parser.add_argument("--global", action="store_true",
|
|
407
|
+
help="print only the global settings, don't include local settings.")
|
|
408
|
+
parser.add_argument("--group", help="print only settings for this group")
|
|
409
|
+
args = parser.parse_args(args or [])
|
|
356
410
|
|
|
357
411
|
# The following import will activate the pretty printing of the AttributeDict
|
|
358
412
|
# through the __rich__ method.
|
|
@@ -360,15 +414,22 @@ if __name__ == "__main__":
|
|
|
360
414
|
from rich import print
|
|
361
415
|
|
|
362
416
|
if args.local:
|
|
363
|
-
location =
|
|
417
|
+
location = get_local_settings_path()
|
|
364
418
|
if location:
|
|
419
|
+
location = str(Path(location).expanduser().resolve())
|
|
365
420
|
settings = Settings.load(filename=location)
|
|
366
421
|
print(settings)
|
|
367
422
|
print(f"Loaded from [purple]{location}.")
|
|
368
423
|
else:
|
|
369
424
|
print("[red]No local settings defined.")
|
|
370
425
|
else:
|
|
371
|
-
|
|
426
|
+
# if the global option is given we don't want to include local settings
|
|
427
|
+
add_local_settings = False if vars(args)["global"] else True
|
|
428
|
+
|
|
429
|
+
if args.group:
|
|
430
|
+
settings = Settings.load(args.group, add_local_settings=add_local_settings)
|
|
431
|
+
else:
|
|
432
|
+
settings = Settings.load(add_local_settings=add_local_settings)
|
|
372
433
|
print(settings)
|
|
373
434
|
print("[blue]Memoized locations:")
|
|
374
435
|
locations = Settings.get_memoized_locations()
|
|
@@ -379,3 +440,9 @@ def get_site_id() -> str:
|
|
|
379
440
|
|
|
380
441
|
site = Settings.load("SITE")
|
|
381
442
|
return site.ID
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
# ignore_m_warning('egse.settings')
|
|
446
|
+
|
|
447
|
+
if __name__ == "__main__":
|
|
448
|
+
main()
|
egse/settings.yaml
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
SSH_SERVER: localhost # The IP address of the SSH server on your site
|
|
4
|
-
SSH_PORT: 22 # The TCP/IP port on which the SSH server is listening
|
|
1
|
+
# This is a stub file. Don't add any settings to this file, use the settings.yaml file
|
|
2
|
+
# in your projects module, like e.g. `cgse_common/settings.yaml` for this module.
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
SITE:
|
|
5
|
+
ID: XXXX
|