cgse-common 2023.1.4__tar.gz → 2024.1.0__tar.gz
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-2024.1.0/.gitignore +18 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/PKG-INFO +25 -22
- cgse_common-2024.1.0/pyproject.toml +59 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/config.py +7 -128
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/control.py +0 -1
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/decorators.py +8 -8
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/env.py +0 -8
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/monitoring.py +1 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/resource.py +70 -2
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/settings.yaml +2 -1
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/system.py +1 -1
- cgse_common-2023.1.4/pyproject.toml +0 -51
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/README.md +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/bits.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/command.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/device.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/exceptions.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/mixin.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/observer.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/obsid.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/persistence.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/plugin.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/process.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/protocol.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/proxy.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/reload.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/services.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/services.yaml +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/settings.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/setup.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/state.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/version.py +0 -0
- {cgse_common-2023.1.4 → cgse_common-2024.1.0}/src/egse/zmq_ser.py +0 -0
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cgse-common
|
|
3
|
-
Version:
|
|
4
|
-
Summary: Software framework to support testing
|
|
5
|
-
Author: Rik Huygen
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
Requires-Dist: PyYAML (>=6.0.1,<7.0.0)
|
|
15
|
-
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
16
|
-
Requires-Dist: deepdiff (>=6.6.1,<7.0.0)
|
|
17
|
-
Requires-Dist: distro (>=1.8.0,<2.0.0)
|
|
18
|
-
Requires-Dist: numpy
|
|
3
|
+
Version: 2024.1.0
|
|
4
|
+
Summary: Software framework to support hardware testing
|
|
5
|
+
Author-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: CGSE,Common-EGSE,hardware testing,software framework
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Requires-Dist: click
|
|
10
|
+
Requires-Dist: deepdiff
|
|
11
|
+
Requires-Dist: distro
|
|
12
|
+
Requires-Dist: gitpython
|
|
13
|
+
Requires-Dist: numpy==1.22.4
|
|
19
14
|
Requires-Dist: pandas
|
|
20
|
-
Requires-Dist: prometheus-client
|
|
21
|
-
Requires-Dist: psutil
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: prometheus-client
|
|
16
|
+
Requires-Dist: psutil
|
|
17
|
+
Requires-Dist: pyyaml
|
|
18
|
+
Requires-Dist: pyzmq==23.2.1
|
|
19
|
+
Requires-Dist: rich
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pipdeptree; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
23
|
+
Requires-Dist: tomlkit; extra == 'dev'
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest; extra == 'test'
|
|
26
|
+
Requires-Dist: pytest-cov; extra == 'test'
|
|
27
|
+
Requires-Dist: pytest-mock; extra == 'test'
|
|
24
28
|
Description-Content-Type: text/markdown
|
|
25
29
|
|
|
26
30
|
# Generic Functionality used in the PLATO Common-EGSE
|
|
@@ -56,4 +60,3 @@ A non-comprehensive list of available functionality:
|
|
|
56
60
|
* **egse.resource**: provides convenience functions to use resources in your code without the need to specify an absolute path
|
|
57
61
|
* **egse.system**: defines convenience functions that provide information on system specific functionality like, file system interactions, timing, operating system interactions, etc.
|
|
58
62
|
* **egse.version**: functionality to retrieve the package version information
|
|
59
|
-
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cgse-common"
|
|
3
|
+
version = "2024.1.0"
|
|
4
|
+
description = "Software framework to support hardware testing"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Rik Huygen", email = "rik.huygen@kuleuven.be"},
|
|
7
|
+
{name = "Sara Regibo", email = "sara.regibo@kuleuven.be"}
|
|
8
|
+
]
|
|
9
|
+
readme = {"file" = "README.md", "content-type" = "text/markdown"}
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
keywords = [
|
|
13
|
+
"CGSE",
|
|
14
|
+
"Common-EGSE",
|
|
15
|
+
"hardware testing",
|
|
16
|
+
"software framework"
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"click",
|
|
20
|
+
"deepdiff",
|
|
21
|
+
"distro",
|
|
22
|
+
"gitpython",
|
|
23
|
+
"numpy == 1.22.4", # only used in setup
|
|
24
|
+
"pandas", # only used in setup
|
|
25
|
+
"prometheus-client",
|
|
26
|
+
"psutil",
|
|
27
|
+
"pyyaml",
|
|
28
|
+
"pyzmq == 23.2.1",
|
|
29
|
+
"rich",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
test = ["pytest", "pytest-mock", "pytest-cov"]
|
|
34
|
+
dev = ["pipdeptree", "tomlkit", "ruff"]
|
|
35
|
+
|
|
36
|
+
[project.entry-points."cgse.version"]
|
|
37
|
+
cgse-common = 'egse'
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.sdist]
|
|
40
|
+
exclude = [
|
|
41
|
+
"/tests",
|
|
42
|
+
"/pytest.ini",
|
|
43
|
+
"/.gitignore",
|
|
44
|
+
"/*poetry*",
|
|
45
|
+
"/*setuptools*",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/egse"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
extend-select = ["E501"]
|
|
56
|
+
|
|
57
|
+
[build-system]
|
|
58
|
+
requires = ["hatchling"]
|
|
59
|
+
build-backend = "hatchling.build"
|
|
@@ -4,13 +4,9 @@ and to find paths and resources.
|
|
|
4
4
|
"""
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
import errno
|
|
8
7
|
import fnmatch
|
|
9
8
|
import logging
|
|
10
9
|
import os
|
|
11
|
-
from functools import lru_cache
|
|
12
|
-
from os.path import exists
|
|
13
|
-
from os.path import join
|
|
14
10
|
from pathlib import Path
|
|
15
11
|
from pathlib import PurePath
|
|
16
12
|
from typing import List
|
|
@@ -18,10 +14,8 @@ from typing import Optional
|
|
|
18
14
|
from typing import Tuple
|
|
19
15
|
from typing import Union
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
HERE = Path(__file__).parent.resolve()
|
|
24
|
-
MODULE_LOGGER = logging.getLogger(__name__)
|
|
17
|
+
_HERE = Path(__file__).parent.resolve()
|
|
18
|
+
_LOGGER = logging.getLogger(__name__)
|
|
25
19
|
|
|
26
20
|
|
|
27
21
|
def find_first_occurrence_of_dir(pattern: str, root: Path | str = None) -> Optional[Path]:
|
|
@@ -43,7 +37,7 @@ def find_first_occurrence_of_dir(pattern: str, root: Path | str = None) -> Optio
|
|
|
43
37
|
"""
|
|
44
38
|
import fnmatch
|
|
45
39
|
|
|
46
|
-
root = Path(root).resolve() if root else
|
|
40
|
+
root = Path(root).resolve() if root else _HERE
|
|
47
41
|
if not root.is_dir():
|
|
48
42
|
root = root.parent
|
|
49
43
|
|
|
@@ -171,7 +165,7 @@ def find_files(pattern: str, root: str = None, in_dir: str = None):
|
|
|
171
165
|
|
|
172
166
|
def find_file(name: str, root: str = None, in_dir: str = None) -> Optional[Path]:
|
|
173
167
|
"""
|
|
174
|
-
Find the path to the given file starting from the root directory of the
|
|
168
|
+
Find the path to the given file starting from the root directory of the
|
|
175
169
|
distribution.
|
|
176
170
|
|
|
177
171
|
Note that if there are more files with the given name found in the distribution,
|
|
@@ -239,122 +233,6 @@ def find_root(
|
|
|
239
233
|
return Path(default) if default is not None else None
|
|
240
234
|
|
|
241
235
|
|
|
242
|
-
@lru_cache(maxsize=16)
|
|
243
|
-
def get_common_egse_root(path: Union[str, PurePath] = None) -> Optional[PurePath]:
|
|
244
|
-
"""
|
|
245
|
-
Returns the absolute path to the installation directory for the Common-EGSE.
|
|
246
|
-
|
|
247
|
-
The algorithm first tries to determine the path from the environment variable
|
|
248
|
-
``PLATO_COMMON_EGSE_PATH``. If this environment variable doesn't exist, the algorithm
|
|
249
|
-
tries to determine the path automatically from (1) the git root if it is a git repository,
|
|
250
|
-
or (2) from the location of this module assuming the installation is done from the
|
|
251
|
-
GitHub distribution.
|
|
252
|
-
|
|
253
|
-
When the optional argument ``path`` is given, that directory will be used to start the
|
|
254
|
-
search for the root folder.
|
|
255
|
-
|
|
256
|
-
At this moment the algorithm does not cache the ``egse_path`` in order to speed up
|
|
257
|
-
the successive calls to this function.
|
|
258
|
-
|
|
259
|
-
Args:
|
|
260
|
-
path (str or Path): a directory as a Path or str [optional]
|
|
261
|
-
|
|
262
|
-
Returns:
|
|
263
|
-
Path: the absolute path to the Common-EGSE installation directory or None
|
|
264
|
-
"""
|
|
265
|
-
_TEST_NAMES = ("pyproject.toml", "setup.py")
|
|
266
|
-
if path is not None:
|
|
267
|
-
return find_root(path, tests=_TEST_NAMES)
|
|
268
|
-
|
|
269
|
-
egse_path: Union[str, PurePath, None] = os.getenv("PLATO_COMMON_EGSE_PATH")
|
|
270
|
-
|
|
271
|
-
if egse_path is None:
|
|
272
|
-
|
|
273
|
-
# The root of the plato-common-egse installation shall be determined from the location
|
|
274
|
-
# of this config module using git commands to find the git root folder.
|
|
275
|
-
# This assumes the user has installed from git/GitHub (which is not always true)!
|
|
276
|
-
#
|
|
277
|
-
# Alternatively, the root directory can be determined from the location of this module
|
|
278
|
-
# by going back in the directory structure until the ``setup.py`` module is found.
|
|
279
|
-
|
|
280
|
-
_THIS_FILE_PATH = Path(__file__).resolve()
|
|
281
|
-
_THIS_FILE_LOCATION = _THIS_FILE_PATH.parent
|
|
282
|
-
|
|
283
|
-
try:
|
|
284
|
-
git_repo = git.Repo(_THIS_FILE_PATH, search_parent_directories=True)
|
|
285
|
-
git_root = git_repo.git.rev_parse("--show-toplevel")
|
|
286
|
-
egse_path = git_root
|
|
287
|
-
except (git.exc.InvalidGitRepositoryError, git.exc.NoSuchPathError):
|
|
288
|
-
MODULE_LOGGER.info("no git repository found, assuming installation from distribution.")
|
|
289
|
-
egse_path = find_root(_THIS_FILE_LOCATION, tests=_TEST_NAMES)
|
|
290
|
-
|
|
291
|
-
MODULE_LOGGER.debug(f"Common-EGSE location is automatically determined: {egse_path}.")
|
|
292
|
-
|
|
293
|
-
else:
|
|
294
|
-
MODULE_LOGGER.debug(
|
|
295
|
-
f"Common-EGSE location determined from environment variable "
|
|
296
|
-
f"PLATO_COMMON_EGSE_PATH: {egse_path}"
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
return Path(egse_path)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
def get_resource_dirs(root_dir: Union[str, PurePath] = None) -> List[Path]:
|
|
303
|
-
"""
|
|
304
|
-
Define directories that contain resources like images, icons, and data files.
|
|
305
|
-
|
|
306
|
-
Resource directories can have the following names: `resources`, `data`, `icons`, or `images`.
|
|
307
|
-
This function checks if any of the resource directories exist in the project root directory,
|
|
308
|
-
in the `root_dir` that is given as an argument or in the `src/egse` sub-folder.
|
|
309
|
-
|
|
310
|
-
So, the directories that are searched for the resource folders are:
|
|
311
|
-
|
|
312
|
-
* `root_dir` or the project's root directory
|
|
313
|
-
* the `src/egse` sub-folder of one of the above
|
|
314
|
-
|
|
315
|
-
For all existing directories the function returns the absolute path.
|
|
316
|
-
|
|
317
|
-
Args:
|
|
318
|
-
root_dir (str): the directory to search for resource folders
|
|
319
|
-
|
|
320
|
-
Returns:
|
|
321
|
-
a list of absolute Paths.
|
|
322
|
-
"""
|
|
323
|
-
project_dir = Path(root_dir).resolve() if root_dir else get_common_egse_root()
|
|
324
|
-
result = []
|
|
325
|
-
for dir_ in ["resources", "data", "icons", "images"]:
|
|
326
|
-
if (project_dir / dir_).is_dir():
|
|
327
|
-
result.append(Path(project_dir, dir_).resolve())
|
|
328
|
-
if (project_dir / "src" / "egse" / dir_).is_dir():
|
|
329
|
-
result.append(Path(project_dir, "src", "egse", dir_).resolve())
|
|
330
|
-
return result
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
def get_resource_path(name: str, resource_root_dir: Union[str, PurePath] = None) -> PurePath:
|
|
334
|
-
"""
|
|
335
|
-
Searches for a data file (resource) with the given name.
|
|
336
|
-
|
|
337
|
-
When `resource_root_dir` is not given, the search for resources will start at the root
|
|
338
|
-
folder of the project (using the function `get_common_egse_root()`). Any other root
|
|
339
|
-
directory can be given, e.g. if you want to start the search from the location of your
|
|
340
|
-
source code file, use `Path(__file__).parent` as the `resource_root_dir` argument.
|
|
341
|
-
|
|
342
|
-
Args:
|
|
343
|
-
name (str): the name of the resource that is requested
|
|
344
|
-
resource_root_dir (str): the root directory where the search for resources should be started
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
the absolute path of the data file with the given name. The first name that matches
|
|
348
|
-
is returned. If no file with the given name or path exists, a FileNotFoundError is raised.
|
|
349
|
-
|
|
350
|
-
"""
|
|
351
|
-
for resource_dir in get_resource_dirs(resource_root_dir):
|
|
352
|
-
resource_path = join(resource_dir, name)
|
|
353
|
-
if exists(resource_path):
|
|
354
|
-
return Path(resource_path).absolute()
|
|
355
|
-
raise FileNotFoundError(errno.ENOENT, f"Could not locate resource '{name}'")
|
|
356
|
-
|
|
357
|
-
|
|
358
236
|
def set_logger_levels(logger_levels: List[Tuple] = None):
|
|
359
237
|
"""
|
|
360
238
|
Set the logging level for the given loggers.
|
|
@@ -368,7 +246,8 @@ def set_logger_levels(logger_levels: List[Tuple] = None):
|
|
|
368
246
|
|
|
369
247
|
|
|
370
248
|
class WorkingDirectory:
|
|
371
|
-
"""
|
|
249
|
+
"""
|
|
250
|
+
WorkingDirectory is a context manager to temporarily change the working directory while
|
|
372
251
|
executing some code.
|
|
373
252
|
|
|
374
253
|
This context manager has a property `path` which returns the absolute path of the
|
|
@@ -402,7 +281,7 @@ class WorkingDirectory:
|
|
|
402
281
|
try:
|
|
403
282
|
os.chdir(self._current_dir)
|
|
404
283
|
except OSError as exc:
|
|
405
|
-
|
|
284
|
+
_LOGGER.warning(f"Change back to previous directory failed: {exc}")
|
|
406
285
|
|
|
407
286
|
@property
|
|
408
287
|
def path(self):
|
|
@@ -13,7 +13,7 @@ from typing import Optional
|
|
|
13
13
|
from egse.settings import Settings
|
|
14
14
|
from egse.system import get_caller_info
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
_LOGGER = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def static_vars(**kwargs):
|
|
@@ -95,7 +95,7 @@ def timer(*, level: int = logging.INFO, precision: int = 4):
|
|
|
95
95
|
value = func(*args, **kwargs)
|
|
96
96
|
end_time = time.perf_counter()
|
|
97
97
|
run_time = end_time - start_time
|
|
98
|
-
|
|
98
|
+
_LOGGER.log(level, f"Finished {func.__name__!r} in {run_time:.{precision}f} secs")
|
|
99
99
|
return value
|
|
100
100
|
|
|
101
101
|
return wrapper_timer
|
|
@@ -160,9 +160,9 @@ def debug(func):
|
|
|
160
160
|
args_repr = [repr(a) for a in args]
|
|
161
161
|
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
|
|
162
162
|
signature = ", ".join(args_repr + kwargs_repr)
|
|
163
|
-
|
|
163
|
+
_LOGGER.debug(f"Calling {func.__name__}({signature})")
|
|
164
164
|
value = func(*args, **kwargs)
|
|
165
|
-
|
|
165
|
+
_LOGGER.debug(f"{func.__name__!r} returned {value!r}")
|
|
166
166
|
else:
|
|
167
167
|
value = func(*args, **kwargs)
|
|
168
168
|
return value
|
|
@@ -240,10 +240,10 @@ def profile(func):
|
|
|
240
240
|
signature = ", ".join(args_repr + kwargs_repr)
|
|
241
241
|
caller = get_caller_info(level=2)
|
|
242
242
|
prefix = f"PROFILE[{profile.counter}]: "
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
_LOGGER.info(f"{prefix}Calling {func.__name__}({signature})")
|
|
244
|
+
_LOGGER.info(f"{prefix} from {caller.filename} at {caller.lineno}.")
|
|
245
245
|
value = func(*args, **kwargs)
|
|
246
|
-
|
|
246
|
+
_LOGGER.info(f"{prefix}{func.__name__!r} returned {value!r}")
|
|
247
247
|
profile.counter -= 1
|
|
248
248
|
else:
|
|
249
249
|
value = func(*args, **kwargs)
|
|
@@ -257,7 +257,7 @@ def to_be_implemented(func):
|
|
|
257
257
|
|
|
258
258
|
@functools.wraps(func)
|
|
259
259
|
def wrapper_tbi(*args, **kwargs):
|
|
260
|
-
|
|
260
|
+
_LOGGER.warning(f"The function/method {func.__name__} is not yet implemented.")
|
|
261
261
|
return func(*args, **kwargs)
|
|
262
262
|
|
|
263
263
|
return wrapper_tbi
|
|
@@ -191,14 +191,6 @@ if __name__ == "__main__":
|
|
|
191
191
|
rich.print("Generated locations and filenames")
|
|
192
192
|
|
|
193
193
|
with all_logging_disabled():
|
|
194
|
-
try:
|
|
195
|
-
rich.print(f" {get_common_egse_root() = !s}", flush=True)
|
|
196
|
-
location = get_common_egse_root()
|
|
197
|
-
if not Path(location).exists():
|
|
198
|
-
rich.print("[red]ERROR: The generated Common-EGSE location doesn't exist![/]")
|
|
199
|
-
except ValueError as exc:
|
|
200
|
-
rich.print(f" get_common_egse_path() = [red]{exc}[/]")
|
|
201
|
-
|
|
202
194
|
try:
|
|
203
195
|
rich.print(f" {get_data_storage_location() = }", flush=True)
|
|
204
196
|
location = get_data_storage_location()
|
|
@@ -75,20 +75,25 @@ their functionality is fully replaced by this `egse.resource` module.
|
|
|
75
75
|
|
|
76
76
|
"""
|
|
77
77
|
|
|
78
|
+
import errno
|
|
78
79
|
import logging
|
|
79
80
|
import re
|
|
80
81
|
from pathlib import Path
|
|
82
|
+
from pathlib import PurePath
|
|
81
83
|
from typing import Dict
|
|
82
84
|
from typing import List
|
|
83
85
|
from typing import Union
|
|
84
86
|
|
|
87
|
+
from os.path import exists
|
|
88
|
+
from os.path import join
|
|
89
|
+
|
|
85
90
|
from egse.config import find_first_occurrence_of_dir
|
|
86
91
|
from egse.config import find_files
|
|
87
92
|
from egse.exceptions import InternalError
|
|
88
93
|
from egse.plugin import entry_points
|
|
89
94
|
from egse.system import get_package_location
|
|
90
95
|
|
|
91
|
-
|
|
96
|
+
_LOGGER = logging.getLogger(__name__)
|
|
92
97
|
|
|
93
98
|
|
|
94
99
|
class ResourceError(Exception):
|
|
@@ -106,6 +111,8 @@ class NoSuchFileError(ResourceError):
|
|
|
106
111
|
__all__ = [
|
|
107
112
|
"get_resource",
|
|
108
113
|
"get_resource_locations",
|
|
114
|
+
"get_resource_dirs",
|
|
115
|
+
"get_resource_path",
|
|
109
116
|
"add_resource_id",
|
|
110
117
|
"initialise_resources",
|
|
111
118
|
"ResourceError",
|
|
@@ -129,6 +136,8 @@ DEFAULT_RESOURCES = {
|
|
|
129
136
|
"data": "/data",
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
_RESOURCE_DIRS = ["resources", "icons", "images", "styles", "data"]
|
|
140
|
+
|
|
132
141
|
resources = {}
|
|
133
142
|
|
|
134
143
|
|
|
@@ -180,6 +189,65 @@ def get_resource_locations() -> Dict[str, List[Path]]:
|
|
|
180
189
|
return resources.copy()
|
|
181
190
|
|
|
182
191
|
|
|
192
|
+
def get_resource_dirs(root_dir: Union[str, PurePath]) -> List[Path]:
|
|
193
|
+
"""
|
|
194
|
+
Define directories that contain resources like images, icons, and data files.
|
|
195
|
+
|
|
196
|
+
Resource directories can have the following names: `resources`, `data`, `icons`, or `images`.
|
|
197
|
+
This function checks if any of the resource directories exist in the `root_dir` that is given as an argument.
|
|
198
|
+
|
|
199
|
+
For all existing directories the function returns the absolute path.
|
|
200
|
+
|
|
201
|
+
If the argument root_dir is None, an empty list will be returned and a warning message will be issued.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
root_dir (str): the directory to search for resource folders
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
a list of absolute Paths.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
if root_dir is None:
|
|
211
|
+
_LOGGER.warning("The argument root_dir can not be None, an empty list is returned.")
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
root_dir = Path(root_dir).resolve()
|
|
215
|
+
if not root_dir.is_dir():
|
|
216
|
+
root_dir = root_dir.parent
|
|
217
|
+
|
|
218
|
+
result = []
|
|
219
|
+
for dir_ in _RESOURCE_DIRS:
|
|
220
|
+
if (root_dir / dir_).is_dir():
|
|
221
|
+
result.append(Path(root_dir, dir_).resolve())
|
|
222
|
+
|
|
223
|
+
return result
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def get_resource_path(name: str, resource_root_dir: Union[str, PurePath] = None) -> PurePath:
|
|
227
|
+
"""
|
|
228
|
+
Searches for a data file (resource) with the given name.
|
|
229
|
+
|
|
230
|
+
When `resource_root_dir` is not given, the search for resources will start at the root
|
|
231
|
+
folder of the project (using the function `get_common_egse_root()`). Any other root
|
|
232
|
+
directory can be given, e.g. if you want to start the search from the location of your
|
|
233
|
+
source code file, use `Path(__file__).parent` as the `resource_root_dir` argument.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
name (str): the name of the resource that is requested
|
|
237
|
+
resource_root_dir (str): the root directory w_HERE the search for resources should be started
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
the absolute path of the data file with the given name. The first name that matches
|
|
241
|
+
is returned. If no file with the given name or path exists, a FileNotFoundError is raised.
|
|
242
|
+
|
|
243
|
+
"""
|
|
244
|
+
for resource_dir in get_resource_dirs(resource_root_dir):
|
|
245
|
+
resource_path = join(resource_dir, name)
|
|
246
|
+
if exists(resource_path):
|
|
247
|
+
return Path(resource_path).absolute()
|
|
248
|
+
raise FileNotFoundError(errno.ENOENT, f"Could not locate resource '{name}'")
|
|
249
|
+
|
|
250
|
+
|
|
183
251
|
def initialise_resources(root: Union[Path, str] = Path(__file__).parent):
|
|
184
252
|
"""
|
|
185
253
|
Initialise the default resources and any resource published by a package entry point.
|
|
@@ -215,7 +283,7 @@ def initialise_resources(root: Union[Path, str] = Path(__file__).parent):
|
|
|
215
283
|
if location not in x:
|
|
216
284
|
x.append(location)
|
|
217
285
|
|
|
218
|
-
|
|
286
|
+
_LOGGER.debug(f"Resources have been initialised: {resources = }")
|
|
219
287
|
|
|
220
288
|
|
|
221
289
|
def print_resources():
|
|
@@ -8,7 +8,7 @@ SITE:
|
|
|
8
8
|
SSH_PORT: 22 # The TCP/IP port on which the SSH server is listening
|
|
9
9
|
|
|
10
10
|
REPO:
|
|
11
|
-
|
|
11
|
+
CGSE_CONF: ENV['CGSE_CONF_REPO_LOCATION']
|
|
12
12
|
|
|
13
13
|
DSI:
|
|
14
14
|
|
|
@@ -308,6 +308,7 @@ Logging Control Server: # LG_CS
|
|
|
308
308
|
COMMANDING_PORT: 7001
|
|
309
309
|
METRICS_PORT: 7003 # The HTTP port where Prometheus will connect to for retrieving metrics
|
|
310
310
|
FILE_STORAGE_LOCATION: ENV['PLATO_LOG_FILE_LOCATION']
|
|
311
|
+
FILENAME: general.log # The name used for the log file in the logging manager
|
|
311
312
|
MAX_NR_LOG_FILES: 20 # The maximum number of log files that will be maintained in a roll-over
|
|
312
313
|
MAX_SIZE_LOG_FILES: 20 # The maximum size one log file can become
|
|
313
314
|
CUTELOG_IP_ADDRESS: 127.0.0.1 # The IP address of the cutelog listening server
|
|
@@ -1064,7 +1064,7 @@ def read_last_line(filename: str | Path, max_line_length=5000):
|
|
|
1064
1064
|
return ""
|
|
1065
1065
|
|
|
1066
1066
|
|
|
1067
|
-
def read_last_lines(filename: str, num_lines: int):
|
|
1067
|
+
def read_last_lines(filename: str | Path, num_lines: int):
|
|
1068
1068
|
"""Return the last lines of a text file.
|
|
1069
1069
|
|
|
1070
1070
|
Args:
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
[tool.poetry]
|
|
2
|
-
name = "cgse-common"
|
|
3
|
-
version = "2023.1.4"
|
|
4
|
-
description = "Software framework to support testing hardware"
|
|
5
|
-
authors = [
|
|
6
|
-
"Rik Huygen <rik.huygen@kuleuven.be>",
|
|
7
|
-
"Sara Regibo <sara.regibo@kuleuven.be>",
|
|
8
|
-
]
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
packages = [
|
|
11
|
-
{ include = "egse", from = "src" }
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
[tool.poetry.dependencies]
|
|
15
|
-
python = "^3.8"
|
|
16
|
-
click = "^8.1.7"
|
|
17
|
-
deepdiff = "^6.6.1"
|
|
18
|
-
distro = "^1.8.0"
|
|
19
|
-
GitPython = "^3.1.40"
|
|
20
|
-
numpy = "*"
|
|
21
|
-
pandas = "*"
|
|
22
|
-
prometheus-client = "^0.17.1"
|
|
23
|
-
psutil = "^5.9.6"
|
|
24
|
-
PyYAML = "^6.0.1"
|
|
25
|
-
pyzmq = "^25.1.1"
|
|
26
|
-
rich = "^13.6.0"
|
|
27
|
-
|
|
28
|
-
[tool.poetry.group.test.dependencies]
|
|
29
|
-
pytest = "^7.4.2"
|
|
30
|
-
pytest-mock = "^3.11.1"
|
|
31
|
-
pytest-cov = "^4.1.0"
|
|
32
|
-
|
|
33
|
-
[tool.poetry.group.dev.dependencies]
|
|
34
|
-
pipdeptree = "^2.13.0"
|
|
35
|
-
ruff = "^0.1.6"
|
|
36
|
-
|
|
37
|
-
[tool.poetry.plugins."cgse.version"]
|
|
38
|
-
cgse-common = 'egse'
|
|
39
|
-
|
|
40
|
-
[tool.ruff]
|
|
41
|
-
line-length = 120
|
|
42
|
-
|
|
43
|
-
[tool.ruff.lint]
|
|
44
|
-
# Add the `line-too-long` rule to the enforced rule set. By default, Ruff omits rules that
|
|
45
|
-
# overlap with the use of a formatter, like Black, but we can override this behavior by
|
|
46
|
-
# explicitly adding the rule.
|
|
47
|
-
extend-select = ["E501"]
|
|
48
|
-
|
|
49
|
-
[build-system]
|
|
50
|
-
requires = ["poetry-core"]
|
|
51
|
-
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|