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.
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.control import Failure
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
@@ -2,9 +2,10 @@
2
2
  This module provides functionality to handle responses from the control servers.
3
3
  """
4
4
  __all__ = [
5
- "Success",
6
5
  "Failure",
7
6
  "Message",
7
+ "Response",
8
+ "Success",
8
9
  ]
9
10
 
10
11
  from typing import Any
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
- SERVICE_SETTINGS = Settings.load(filename="services.yaml")
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. The Settings class can read from different YAML files. By default,
7
- settings are loaded from a file called ``settings.yaml``. The default yaml configuration file is
8
- located in the same directory as this module.
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
- made available as instance variables of the returned class.
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
- ``dsi_settings``. The returned class is and behaves also like a dictionary, so you can check
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 ``filename=`` keyword.
50
- If that file is located at a specific location, also use the ``location=`` keyword.
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 complete YAML file, i.e. all the groups into a dictionary.
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.exceptions import FileIsEmptyError
68
- from egse.system import AttributeDict
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 read_configuration_file(cls, filename: str, *, force=False):
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 get_memoized_locations(cls):
164
- return cls.__memoized_yaml.keys()
254
+ def is_memoized(cls, filename: str) -> bool:
255
+ return filename in cls.__memoized_yaml
165
256
 
166
257
  @classmethod
167
- def load(cls, group_name=None, filename="settings.yaml", location=None, *, force=False,
168
- add_local_settings=True):
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
- The default YAML file is 'settings.yaml' and is located in the same directory
174
- as the settings module.
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
- * when an absolute location is given, that location is used 'as is'.
265
+ @classmethod
266
+ def clear_memoized(cls):
267
+ cls.__memoized_yaml.clear()
185
268
 
186
- Args:
187
- group_name (str): the name of one of the main groups from the YAML file
188
- filename (str): the name of the YAML file to read
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
- Returns:
194
- a dynamically created class with the configuration parameters as instance variables.
273
+ @classmethod
274
+ def profiling(cls):
275
+ return cls.__profile
195
276
 
196
- Raises:
197
- a SettingsError when the group is not defined in the YAML file.
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
- _THIS_FILE_LOCATION = pathlib.Path(__file__).resolve().parent
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
- if (caller_dir / filename).is_file():
211
- yaml_location = caller_dir
212
- else:
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
- # The location was given as an argument
294
+ return global_settings
217
295
 
218
- yaml_location = pathlib.Path(location).resolve()
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
- _LOGGER.debug(f"yaml_location in Settings.load(location={location}) is {yaml_location}")
304
+ global_settings = load_global_settings(entry_point, force)
221
305
 
222
- # Load the YAML global document
306
+ group_settings = attrdict(label=group_name)
223
307
 
224
- try:
225
- yaml_document_global = cls.read_configuration_file(
226
- str(yaml_location / filename), force=force
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 not yaml_document_global:
236
- raise SettingsError(f"Empty YAML document {filename} at {yaml_location}.")
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
- # Load the LOCAL settings YAML file
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
- local_settings = {}
324
+ return group_settings
241
325
 
242
- if add_local_settings:
243
- try:
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
- # Check if the requested group is defined in the YAML document
329
+ return load_settings_file(Path(location).expanduser(), filename, force)
276
330
 
277
- if group_name not in yaml_document_global:
278
- raise SettingsError(
279
- f"Group name '{group_name}' is not defined in the YAML "
280
- f"document '{filename}' at '{yaml_location}."
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
- # Check if the group has any settings
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
- if not yaml_document_global[group_name]:
286
- raise SettingsError(f"Empty group in YAML document {filename} at {yaml_location}.")
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
- group_settings = AttributeDict(
289
- {name: value for name, value in yaml_document_global[group_name].items()}
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
- if add_local_settings and group_name in local_settings:
293
- recursive_dict_update(group_settings, local_settings[group_name])
354
+ Returns:
355
+ a dynamically created class with the configuration parameters as instance variables.
294
356
 
295
- return group_settings
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 the what your choices are.
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
- args = parser.parse_args()
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 = get_local_settings()
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
- settings = Settings.load()
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
- SITE:
2
- ID: XXX
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
- PROCESS:
7
- METRICS_INTERVAL: 10
4
+ SITE:
5
+ ID: XXXX