cgse-common 2024.1.1__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/env.py ADDED
@@ -0,0 +1,279 @@
1
+ """
2
+ This module provides functionality to work with and check your Python environment.
3
+ """
4
+ from pathlib import Path
5
+
6
+ from egse.system import all_logging_disabled
7
+ from egse.system import ignore_m_warning
8
+
9
+ ENV_PLATO_COMMON_EGSE = "PLATO_COMMON_EGSE_PATH"
10
+ ENV_PLATO_INSTALL = "PLATO_INSTALL_LOCATION"
11
+ ENV_PLATO_CONF_DATA = "PLATO_CONF_DATA_LOCATION"
12
+ ENV_PLATO_CONF_REPO = "PLATO_CONF_REPO_LOCATION"
13
+ ENV_PLATO_STORAGE_DATA = "PLATO_DATA_STORAGE_LOCATION"
14
+ ENV_PLATO_LOG_DATA = "PLATO_LOG_FILE_LOCATION"
15
+ ENV_PLATO_LOCAL_SETTINGS = "PLATO_LOCAL_SETTINGS"
16
+
17
+ PLATO_ENV_VARIABLES = [globals()[x] for x in globals() if x.startswith('ENV_PLATO_')]
18
+
19
+ __all__ = [
20
+ "get_data_storage_location",
21
+ "get_conf_data_location",
22
+ "get_log_file_location",
23
+ *PLATO_ENV_VARIABLES
24
+ ]
25
+
26
+
27
+ def get_data_storage_location(setup=None, site_id: str = None) -> str:
28
+ """
29
+ Returns the full path of the data storage location for the Site as
30
+ in the given Setup. If the Setup is not given, it is requested from the
31
+ configuration manager unless the `site_id` argument is given.
32
+
33
+ Note: when you specify the `site_id` as an argument, it takes precedence
34
+ over the site_id that is specified in the Setup.
35
+
36
+ Args:
37
+ setup: the Setup from which the Camera name and Site ID are taken
38
+ site_id: the site identifier (to be used instead of the site_id in the Setup)
39
+
40
+ Returns:
41
+ The full path of data storage location as a string.
42
+
43
+ Raises:
44
+ A ValueError when no Setup can be loaded.
45
+ """
46
+
47
+ # FIXME: this should be made independent of PLATO, maybe use CGSE_STORAGE_LOCATION as environment variable.
48
+
49
+ # FIXME: use CGSE_SITE_ID if Setup can not be determined.
50
+
51
+ import os
52
+
53
+ if site_id is None:
54
+
55
+ from egse.setup import Setup
56
+ from egse.state import GlobalState
57
+ setup: Setup = setup or GlobalState.setup
58
+
59
+ if setup is None:
60
+ raise ValueError(
61
+ "Could not determine Setup, which is None, even after loading from the configuration manager."
62
+ )
63
+
64
+ site = setup.site_id
65
+ else:
66
+ site = site_id
67
+
68
+ data_root = os.environ[ENV_PLATO_STORAGE_DATA]
69
+ data_root = data_root.rstrip('/')
70
+
71
+ return data_root if data_root.endswith(site) else f"{data_root}/{site}"
72
+
73
+
74
+ def get_conf_data_location(setup=None) -> str:
75
+ """
76
+ Returns the full path of the location of the Setups for the Site.
77
+ If the Setup is not given, it is requested from the configuration manager.
78
+
79
+ Args:
80
+ setup: the Setup from which the Camera name and Site ID are taken
81
+
82
+ Returns:
83
+ The full path of location of the Setups as a string.
84
+
85
+ Raises:
86
+ A ValueError when no Setup can be loaded.
87
+ """
88
+
89
+ data_root = get_data_storage_location(setup=setup)
90
+
91
+ return f"{data_root}/conf"
92
+
93
+
94
+ def get_log_file_location() -> str:
95
+ """
96
+ Returns the full path of the location of the log files. The log file location is read from the environment
97
+ variable PLATO_LOG_FILE_LOCATION. The location shall be independent of the Setup, Camera ID or any other
98
+ setting that is subject to change.
99
+
100
+ If the environment variable is not set, a default log file location is created from the data storage location as
101
+ follows: $PLATO_DATA_STORAGE_LOCATION/<SITE_ID>/log.
102
+
103
+ Returns:
104
+ The full path of location of the log files as a string.
105
+ """
106
+
107
+ # FIXME: this should be made independent of PLATO, maybe put the log file in the cwd unless an environment
108
+ # variable CGSE_LOG_FILE_LOCATION is defined and the location exists and is writable.
109
+
110
+ import os
111
+
112
+ try:
113
+ log_data_root = os.environ[ENV_PLATO_LOG_DATA]
114
+ except KeyError:
115
+ data_root = os.environ[ENV_PLATO_STORAGE_DATA]
116
+ data_root = data_root.rstrip('/')
117
+
118
+ from egse.settings import get_site_id
119
+ site = get_site_id()
120
+
121
+ log_data_root = f"{data_root}/{site}/log"
122
+
123
+ return log_data_root
124
+
125
+
126
+ ignore_m_warning('egse.env')
127
+
128
+ if __name__ == "__main__":
129
+
130
+ import argparse
131
+ import os
132
+ import sys
133
+ import rich
134
+
135
+ from egse.config import get_common_egse_root
136
+
137
+ parser = argparse.ArgumentParser()
138
+ parser.add_argument(
139
+ "--full",
140
+ default=False,
141
+ action="store_true",
142
+ help="Print a full report on environment variables and paths.",
143
+ )
144
+ parser.add_argument(
145
+ "--doc",
146
+ default=False,
147
+ action="store_true",
148
+ help="Print help on the environment variables and paths.",
149
+ )
150
+
151
+ args = parser.parse_args()
152
+
153
+ def check_env_dir(env_var: str):
154
+
155
+ value = os.environ.get(env_var)
156
+
157
+ if value is None:
158
+ value = "[bold red]not set"
159
+ elif not value.startswith('/'):
160
+ value = f"[default]{value} [bold orange3](this is a relative path!)"
161
+ elif not os.path.exists(value):
162
+ value = f"[default]{value} [bold red](location doesn't exist!)"
163
+ elif not os.path.isdir(value):
164
+ value = f"[default]{value} [bold red](location is not a directory!)"
165
+ else:
166
+ value = f"[default]{value}"
167
+ return value
168
+
169
+
170
+ def check_env_file(env_var: str):
171
+
172
+ value = os.environ.get(env_var)
173
+
174
+ if value is None:
175
+ value = "[bold red]not set"
176
+ elif not os.path.exists(value):
177
+ value = f"[default]{value} [bold red](location doesn't exist!)"
178
+ else:
179
+ value = f"[default]{value}"
180
+ return value
181
+
182
+ rich.print("Environment variables:")
183
+
184
+ for var in PLATO_ENV_VARIABLES:
185
+ if var.endswith("_SETTINGS"):
186
+ rich.print(f" {var} = {check_env_file(var)}")
187
+ else:
188
+ rich.print(f" {var} = {check_env_dir(var)}")
189
+
190
+ rich.print()
191
+ rich.print("Generated locations and filenames")
192
+
193
+ with all_logging_disabled():
194
+ try:
195
+ rich.print(f" {get_data_storage_location() = }", flush=True)
196
+ location = get_data_storage_location()
197
+ if not Path(location).exists():
198
+ rich.print("[red]ERROR: The generated data storage location doesn't exist![/]")
199
+ except ValueError as exc:
200
+ rich.print(f" get_data_storage_location() = [red]{exc}[/]")
201
+
202
+ try:
203
+ rich.print(f" {get_conf_data_location() = }", flush=True)
204
+ location = get_conf_data_location()
205
+ if not Path(location).exists():
206
+ rich.print("[red]ERROR: The generated configuration data location doesn't exist![/]")
207
+ except ValueError as exc:
208
+ rich.print(f" get_conf_data_location() = [red]{exc}[/]")
209
+
210
+ try:
211
+ rich.print(f" {get_log_file_location() = }", flush=True)
212
+ location = get_log_file_location()
213
+ if not Path(location).exists():
214
+ rich.print("[red]ERROR: The generated log files location doesn't exist![/]")
215
+ except ValueError as exc:
216
+ rich.print(f" get_log_file_location() = [red]{exc}[/]")
217
+
218
+ if args.full:
219
+ rich.print()
220
+ rich.print(f" PYTHONPATH=[default]{os.environ.get('PYTHONPATH')}")
221
+ rich.print(f" PYTHONSTARTUP=[default]{os.environ.get('PYTHONSTARTUP')}")
222
+ rich.print()
223
+ python_path_msg = "\n ".join(sys.path)
224
+ rich.print(f" sys.path=[\n {python_path_msg}\n ]")
225
+ path_msg = "\n ".join(os.environ.get("PATH").split(":"))
226
+ rich.print(f" PATH=[\n {path_msg}\n ]")
227
+
228
+ help_msg = f"""
229
+ [bold]{ENV_PLATO_COMMON_EGSE}[/bold]:
230
+ This variable should point to the root of the working copy of the 'plato-common-egse'
231
+ project. Its value is usually '~/git/plato-common-egse' which is considered the default
232
+ location.
233
+
234
+ [bold]{ENV_PLATO_INSTALL}[/bold]:
235
+ This variable shall point to the location where the CGSE will be installed and is
236
+ usually set to `/cgse`. The variable is used by the [blue]update_cgse[/blue] script.
237
+
238
+ [bold]{ENV_PLATO_CONF_DATA}[/bold]:
239
+ This directory is the root folder for all the Setups of the site, the site is part
240
+ of the name. By default, this directory is located in the overall data storage folder.
241
+
242
+ [bold]{ENV_PLATO_CONF_REPO}[/bold]:
243
+ This variable is the root of the working copy of the 'plato-cgse-conf' project.
244
+ The value is usually set to `~/git/plato-cgse-conf`.
245
+
246
+ [bold]{ENV_PLATO_STORAGE_DATA}[/bold]:
247
+ This directory contains all the data files from the control servers and other
248
+ components. This folder is the root folder for all data from all cameras and
249
+ all sites. Below this folder shall be a folder for each of the cameras and in
250
+ there a sub-folder for each of the sites where that camera was tested. The
251
+ hierarchy is therefore: `$PLATO_DATA_STORAGE_LOCATION/<camera name>/<site id>.
252
+ Each of those folder shall contain at least the sub-folder [blue]daily[/blue], and [blue]obs[/blue].
253
+
254
+ There is also a file called [blue]obsid-table-<site id>.txt[/blue] which is maintained by
255
+ the configuration manager and contains information about the observations that
256
+ were run and the commands to start those observation.
257
+
258
+ [bold]{ENV_PLATO_LOG_DATA}[/bold]:
259
+ This directory contains the log files with all messages that were sent to the
260
+ logger control server. The log files are rotated on a daily basis at midnight UTC.
261
+ By default, this directory is also located in the overall data storage folder.
262
+
263
+ [bold]{ENV_PLATO_LOCAL_SETTINGS}[/bold]:
264
+ This file is used for local site-specific settings. When the environment
265
+ variable is not set, no local settings will be loaded. By default, this variable
266
+ is assumed to be '/cgse/local_settings.yaml'.
267
+ """
268
+
269
+ if args.doc:
270
+ rich.print(help_msg)
271
+
272
+ if not args.full:
273
+ rich.print()
274
+ rich.print("use the '--full' flag to get a more detailed report, '--doc' for help on the variables.")
275
+
276
+ # Do we still use these environment variables?
277
+ #
278
+ # PLATO_WORKDIR
279
+ # PLATO_COMMON_EGSE_PATH - YES
egse/exceptions.py ADDED
@@ -0,0 +1,88 @@
1
+ """
2
+ The Exception Hierarchy:
3
+
4
+ Exception
5
+ +-- CommonEGSEException
6
+ +-- Warning
7
+ +-- Error
8
+ +-- InvalidOperationError
9
+ +-- DeviceNotFoundError (should move to subclass of DeviceError)
10
+ +-- InternalStateError
11
+ +-- DeviceError
12
+ +-- DeviceControllerError
13
+ +-- DeviceConnectionError
14
+ +-- DeviceTimeoutError
15
+ +-- DeviceInterfaceError
16
+ +-- Failure
17
+ +-- HexapodError
18
+ +-- PMACError
19
+ +-- OGSEError
20
+ +-- ESLError
21
+ +-- FilterWheelError
22
+ +-- FilterWheel8smc4Error
23
+ +-- ShutterKSC1010Error
24
+ +-- WindowSizeError
25
+ +-- SettingsError
26
+ +-- StagesError
27
+
28
+ """
29
+
30
+
31
+ class CommonEGSEException(Exception):
32
+ """The base exception for all errors and warnings in the Common-EGSE."""
33
+
34
+ pass
35
+
36
+
37
+ class Error(CommonEGSEException):
38
+ """The base class for all Common-EGSE Errors."""
39
+
40
+ pass
41
+
42
+
43
+ class Warning(CommonEGSEException):
44
+ """The base class for all Common-EGSE Warnings."""
45
+
46
+ pass
47
+
48
+
49
+ class FileIsEmptyError(Error):
50
+ """Raised when a file is empty and that is unexpected."""
51
+ pass
52
+
53
+
54
+ class InvalidOperationError(Error):
55
+ """
56
+ Raised when a certain operation is not valid in the given state,
57
+ circumstances or environment.
58
+ """
59
+
60
+ pass
61
+
62
+
63
+ class InvalidInputError(Error):
64
+ """ Exception raised when the input is invalid after editing."""
65
+
66
+ pass
67
+
68
+
69
+ class DeviceNotFoundError(Error):
70
+ """Raised when a device could not be located, or loaded."""
71
+
72
+ pass
73
+
74
+
75
+ class InternalStateError(Error):
76
+ """Raised when an object encounters an internal state inconsistency."""
77
+
78
+ pass
79
+
80
+
81
+ class InternalError(Error):
82
+ """Raised when an internal inconsistency occurred in a function, method or class."""
83
+
84
+ pass
85
+
86
+
87
+ class Abort(RuntimeError):
88
+ """Internal Exception to signal a process to abort."""