cgse-common 2023.1.5__py3-none-any.whl → 2024.1.4__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-2023.1.5.dist-info → cgse_common-2024.1.4.dist-info}/METADATA +28 -25
- cgse_common-2024.1.4.dist-info/RECORD +36 -0
- {cgse_common-2023.1.5.dist-info → cgse_common-2024.1.4.dist-info}/WHEEL +1 -1
- cgse_common-2024.1.4.dist-info/entry_points.txt +2 -0
- egse/bits.py +266 -41
- egse/calibration.py +250 -0
- egse/command.py +10 -29
- egse/config.py +17 -12
- egse/control.py +0 -81
- egse/decorators.py +8 -8
- egse/device.py +3 -1
- egse/env.py +411 -106
- egse/hk.py +794 -0
- egse/metrics.py +106 -0
- egse/resource.py +70 -2
- egse/response.py +101 -0
- egse/settings.py +33 -31
- egse/settings.yaml +0 -974
- egse/setup.py +116 -81
- egse/system.py +32 -13
- cgse_common-2023.1.5.dist-info/RECORD +0 -32
- cgse_common-2023.1.5.dist-info/entry_points.txt +0 -3
egse/setup.py
CHANGED
|
@@ -44,10 +44,10 @@ Some of the information in the Setup is interpreted in a special way, i.e. some
|
|
|
44
44
|
processed before returning. Examples are the device classes and calibration/data files. The
|
|
45
45
|
following values are treated special if they start with:
|
|
46
46
|
|
|
47
|
-
* `class//`: the class
|
|
48
|
-
* `csv//`: the CSV file
|
|
49
|
-
* `yaml//`: the YAML file
|
|
50
|
-
* `enum//`: the enumeration
|
|
47
|
+
* `class//`: instantiate the class and return the object
|
|
48
|
+
* `csv//`: load the CSV file and return a numpy array
|
|
49
|
+
* `yaml//`: load the YAML file and return a dictionary
|
|
50
|
+
* `enum//`: dynamically create the enumeration and return the Enum object
|
|
51
51
|
|
|
52
52
|
#### Device Classes
|
|
53
53
|
|
|
@@ -63,7 +63,7 @@ return the device object. As an example, the following defines the Hexapod devic
|
|
|
63
63
|
>>> setup.gse.hexapod.device.is_homing_done()
|
|
64
64
|
False
|
|
65
65
|
>>> setup.gse.hexapod.device.info() # doctest: +ELLIPSIS
|
|
66
|
-
'Info about the PunaSimulator...
|
|
66
|
+
'Info about the PunaSimulator...'
|
|
67
67
|
|
|
68
68
|
In the above example you see that we can call the `is_homing_done()` and `info()` methodes
|
|
69
69
|
directly on the device by navigating the Setup. It would however be better (more performant) to
|
|
@@ -99,8 +99,21 @@ Note: the resource location is always relative to the path defined by the PLATO_
|
|
|
99
99
|
environment variable.
|
|
100
100
|
|
|
101
101
|
"""
|
|
102
|
+
|
|
102
103
|
from __future__ import annotations
|
|
103
104
|
|
|
105
|
+
__all__ = [
|
|
106
|
+
"Setup",
|
|
107
|
+
"navdict", # noqa: ignore typo
|
|
108
|
+
"list_setups",
|
|
109
|
+
"load_setup",
|
|
110
|
+
"get_setup",
|
|
111
|
+
"submit_setup",
|
|
112
|
+
"SetupError",
|
|
113
|
+
"load_last_setup_id",
|
|
114
|
+
"save_last_setup_id",
|
|
115
|
+
]
|
|
116
|
+
|
|
104
117
|
import enum
|
|
105
118
|
import importlib
|
|
106
119
|
import logging
|
|
@@ -117,7 +130,11 @@ import rich
|
|
|
117
130
|
import yaml
|
|
118
131
|
from rich.tree import Tree
|
|
119
132
|
|
|
120
|
-
from egse.
|
|
133
|
+
from egse.env import get_conf_repo_location
|
|
134
|
+
from egse.env import get_conf_repo_location_env_name
|
|
135
|
+
from egse.env import get_data_storage_location
|
|
136
|
+
from egse.response import Failure
|
|
137
|
+
from egse.env import get_conf_data_location
|
|
121
138
|
from egse.system import format_datetime
|
|
122
139
|
from egse.system import sanity_check
|
|
123
140
|
from egse.system import walk_dict_tree
|
|
@@ -131,9 +148,10 @@ class SetupError(Exception):
|
|
|
131
148
|
|
|
132
149
|
|
|
133
150
|
def _load_class(class_name: str):
|
|
134
|
-
"""
|
|
151
|
+
"""
|
|
152
|
+
Find and returns a class based on the fully qualified name.
|
|
135
153
|
|
|
136
|
-
A class name can be preceded with the string `class//`. This is used in YAML
|
|
154
|
+
A class name can be preceded with the string `class//` or `factory//`. This is used in YAML
|
|
137
155
|
files where the class is then instantiated on load.
|
|
138
156
|
|
|
139
157
|
Args:
|
|
@@ -155,7 +173,7 @@ def _load_csv(resource_name: str):
|
|
|
155
173
|
|
|
156
174
|
parts = resource_name[5:].rsplit("/", 1)
|
|
157
175
|
[in_dir, fn] = parts if len(parts) > 1 else [None, parts[0]]
|
|
158
|
-
conf_location =
|
|
176
|
+
conf_location = get_conf_data_location()
|
|
159
177
|
try:
|
|
160
178
|
csv_location = Path(conf_location) / in_dir / fn
|
|
161
179
|
content = genfromtxt(csv_location, delimiter=",", skip_header=1)
|
|
@@ -198,7 +216,7 @@ def _load_yaml(resource_name: str):
|
|
|
198
216
|
|
|
199
217
|
parts = resource_name[6:].rsplit("/", 1)
|
|
200
218
|
[in_dir, fn] = parts if len(parts) > 1 else [None, parts[0]]
|
|
201
|
-
conf_location =
|
|
219
|
+
conf_location = get_conf_data_location()
|
|
202
220
|
try:
|
|
203
221
|
yaml_location = Path(conf_location) / in_dir / fn
|
|
204
222
|
content = NavigableDict(Settings.load(filename=yaml_location, add_local_settings=False))
|
|
@@ -246,22 +264,19 @@ def _parse_filename_for_setup_id(filename: str):
|
|
|
246
264
|
|
|
247
265
|
try:
|
|
248
266
|
return match[2] # match[2] is setup_id
|
|
249
|
-
except (IndexError, TypeError)
|
|
267
|
+
except (IndexError, TypeError):
|
|
250
268
|
return None
|
|
251
269
|
|
|
252
270
|
|
|
253
271
|
def get_last_setup_id_file_path(site_id: str = None) -> Path:
|
|
254
272
|
"""
|
|
255
273
|
Return the fully expanded file path of the file containing the last loaded Setup in the configuration manager.
|
|
274
|
+
The default location for this file is the data storage location.
|
|
256
275
|
|
|
257
276
|
Args:
|
|
258
|
-
site_id: The SITE identifier
|
|
277
|
+
site_id: The SITE identifier (overrides the SITE_ID environment variable)
|
|
259
278
|
|
|
260
279
|
"""
|
|
261
|
-
from egse.env import get_data_storage_location
|
|
262
|
-
from egse.settings import Settings
|
|
263
|
-
|
|
264
|
-
site_id = site_id or Settings.load("SITE").ID
|
|
265
280
|
location = get_data_storage_location(site_id=site_id)
|
|
266
281
|
|
|
267
282
|
return Path(location).expanduser().resolve() / "last_setup_id.txt"
|
|
@@ -510,11 +525,10 @@ class NavigableDict(dict):
|
|
|
510
525
|
ValueError: when the key doesn't start with an underscore.
|
|
511
526
|
"""
|
|
512
527
|
if not key.startswith("_"):
|
|
513
|
-
raise ValueError(
|
|
514
|
-
|
|
515
|
-
)
|
|
528
|
+
raise ValueError(f"Invalid argument key='{key}', must start with underscore character '_'.")
|
|
529
|
+
|
|
516
530
|
try:
|
|
517
|
-
self.__dict__[key]
|
|
531
|
+
_ = self.__dict__[key]
|
|
518
532
|
return True
|
|
519
533
|
except KeyError:
|
|
520
534
|
return False
|
|
@@ -614,11 +628,15 @@ class NavigableDict(dict):
|
|
|
614
628
|
return list(self.__dict__["_memoized"].keys())
|
|
615
629
|
|
|
616
630
|
|
|
631
|
+
navdict = NavigableDict # noqa: ignore typo
|
|
632
|
+
"""Shortcut for NavigableDict and more Pythonic."""
|
|
633
|
+
|
|
634
|
+
|
|
617
635
|
class Setup(NavigableDict):
|
|
618
636
|
"""The Setup class represents a version of the configuration of the test facility, the
|
|
619
637
|
test setup and the Camera Under Test (CUT)."""
|
|
620
638
|
|
|
621
|
-
def __init__(self, nav_dict: NavigableDict = None):
|
|
639
|
+
def __init__(self, nav_dict: NavigableDict | dict = None):
|
|
622
640
|
super().__init__(nav_dict or {})
|
|
623
641
|
|
|
624
642
|
@staticmethod
|
|
@@ -722,10 +740,9 @@ class Setup(NavigableDict):
|
|
|
722
740
|
@staticmethod
|
|
723
741
|
def compare(setup_1: NavigableDict, setup_2: NavigableDict):
|
|
724
742
|
from egse.device import DeviceInterface
|
|
725
|
-
from egse.dpu import DPUSimulator
|
|
726
743
|
from deepdiff import DeepDiff
|
|
727
744
|
|
|
728
|
-
return DeepDiff(setup_1, setup_2, exclude_types=
|
|
745
|
+
return DeepDiff(setup_1, setup_2, exclude_types=[DeviceInterface])
|
|
729
746
|
|
|
730
747
|
# def get_devices(self):
|
|
731
748
|
# """Returns a list of devices for the current setup.
|
|
@@ -741,7 +758,7 @@ class Setup(NavigableDict):
|
|
|
741
758
|
# return devices
|
|
742
759
|
|
|
743
760
|
@staticmethod
|
|
744
|
-
def find_devices(node: NavigableDict, devices=
|
|
761
|
+
def find_devices(node: NavigableDict, devices: dict = None):
|
|
745
762
|
"""
|
|
746
763
|
Returns a dictionary with the devices that are included in the setup. The keys
|
|
747
764
|
in the dictionary are taken from the "device_name" entries in the setup file. The
|
|
@@ -755,6 +772,7 @@ class Setup(NavigableDict):
|
|
|
755
772
|
Returns:
|
|
756
773
|
- Dictionary with the devices that are included in the setup.
|
|
757
774
|
"""
|
|
775
|
+
devices = devices or {}
|
|
758
776
|
|
|
759
777
|
for sub_node in node.values():
|
|
760
778
|
|
|
@@ -848,7 +866,11 @@ def list_setups(**attr):
|
|
|
848
866
|
>>> list_setups(gse__hexapod__ID=4)
|
|
849
867
|
"""
|
|
850
868
|
|
|
851
|
-
|
|
869
|
+
try:
|
|
870
|
+
from egse.confman import ConfigurationManagerProxy
|
|
871
|
+
except ImportError:
|
|
872
|
+
print("WARNING: package 'cgse-core' is not installed, service not available.")
|
|
873
|
+
return
|
|
852
874
|
|
|
853
875
|
try:
|
|
854
876
|
with ConfigurationManagerProxy() as proxy:
|
|
@@ -873,16 +895,18 @@ def get_setup(setup_id: int = None):
|
|
|
873
895
|
This function is for interactive use and consults the configuration manager server. Don't use
|
|
874
896
|
this within the test script, but use the `GlobalState.setup` property instead.
|
|
875
897
|
"""
|
|
876
|
-
|
|
898
|
+
try:
|
|
899
|
+
from egse.confman import ConfigurationManagerProxy
|
|
900
|
+
except ImportError:
|
|
901
|
+
print("WARNING: package 'cgse-core' is not installed, service not available.")
|
|
902
|
+
return
|
|
877
903
|
|
|
878
904
|
try:
|
|
879
905
|
with ConfigurationManagerProxy() as proxy:
|
|
880
906
|
setup = proxy.get_setup(setup_id)
|
|
881
907
|
return setup
|
|
882
|
-
except ConnectionError
|
|
883
|
-
print(
|
|
884
|
-
"Could not make a connection with the Configuration Manager, no Setup returned."
|
|
885
|
-
)
|
|
908
|
+
except ConnectionError:
|
|
909
|
+
print("Could not make a connection with the Configuration Manager, no Setup returned.")
|
|
886
910
|
|
|
887
911
|
|
|
888
912
|
def _check_conditions_for_get_path_of_setup_file(site_id: str) -> Path:
|
|
@@ -911,8 +935,9 @@ def _check_conditions_for_get_path_of_setup_file(site_id: str) -> Path:
|
|
|
911
935
|
NotADirectoryError when either the repository folder or the Setups folder doesn't exist.
|
|
912
936
|
|
|
913
937
|
"""
|
|
914
|
-
repo_location_env =
|
|
915
|
-
|
|
938
|
+
repo_location_env = get_conf_repo_location_env_name()
|
|
939
|
+
|
|
940
|
+
if not (repo_location := get_conf_repo_location()):
|
|
916
941
|
raise LookupError(
|
|
917
942
|
f"Environment variable doesn't exist, please define {repo_location_env} and try again."
|
|
918
943
|
)
|
|
@@ -940,8 +965,8 @@ def get_path_of_setup_file(setup_id: int, site_id: str) -> Path:
|
|
|
940
965
|
Returns the Path to the last Setup file for the given site_id. The last Setup file is the file
|
|
941
966
|
with the largest setup_id number.
|
|
942
967
|
|
|
943
|
-
This function needs the environment variable
|
|
944
|
-
location of the repository
|
|
968
|
+
This function needs the environment variable <PROJECT>_CONF_REPO_LOCATION to be defined as the
|
|
969
|
+
location of the repository with configuration data on your disk.
|
|
945
970
|
|
|
946
971
|
Args:
|
|
947
972
|
setup_id (int): the identifier for the requested Setup
|
|
@@ -1019,7 +1044,11 @@ def load_setup(
|
|
|
1019
1044
|
|
|
1020
1045
|
# When we arrive here the Setup shall be loaded from the Configuration manager
|
|
1021
1046
|
|
|
1022
|
-
|
|
1047
|
+
try:
|
|
1048
|
+
from egse.confman import ConfigurationManagerProxy
|
|
1049
|
+
except ImportError:
|
|
1050
|
+
print("WARNING: package 'cgse-core' is not installed, service not available. Returning an empty Setup.")
|
|
1051
|
+
return Setup()
|
|
1023
1052
|
|
|
1024
1053
|
if setup_id is not None:
|
|
1025
1054
|
try:
|
|
@@ -1064,7 +1093,11 @@ def submit_setup(setup: Setup, description: str):
|
|
|
1064
1093
|
# be replaced by this new Setup. [default=True]
|
|
1065
1094
|
replace: bool = True
|
|
1066
1095
|
|
|
1067
|
-
|
|
1096
|
+
try:
|
|
1097
|
+
from egse.confman import ConfigurationManagerProxy
|
|
1098
|
+
except ImportError:
|
|
1099
|
+
print("WARNING: package 'cgse-core' is not installed, service not available.")
|
|
1100
|
+
return
|
|
1068
1101
|
|
|
1069
1102
|
try:
|
|
1070
1103
|
with ConfigurationManagerProxy() as proxy:
|
|
@@ -1076,27 +1109,27 @@ def submit_setup(setup: Setup, description: str):
|
|
|
1076
1109
|
rich.print(f"[red]Submit failed for given Setup[/red]: {setup}")
|
|
1077
1110
|
setup = None
|
|
1078
1111
|
elif replace:
|
|
1079
|
-
rich.print(textwrap.dedent(
|
|
1080
|
-
f"""\
|
|
1112
|
+
rich.print(textwrap.dedent("""\
|
|
1081
1113
|
[green]
|
|
1082
|
-
Your new setup has been submitted and pushed to GitHub. The new setup is also
|
|
1114
|
+
Your new setup has been submitted and pushed to GitHub. The new setup is also
|
|
1083
1115
|
activated in the configuration manager. Load the new setup in your session with:
|
|
1084
1116
|
|
|
1085
|
-
setup = load_setup()
|
|
1117
|
+
setup = load_setup()
|
|
1086
1118
|
[/]
|
|
1087
1119
|
"""
|
|
1088
1120
|
))
|
|
1089
1121
|
else:
|
|
1090
|
-
rich.print(
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1122
|
+
rich.print(
|
|
1123
|
+
textwrap.dedent(
|
|
1124
|
+
"""[dark_orange]
|
|
1125
|
+
Your new setup has been submitted and pushed to GitHub, but has not been
|
|
1126
|
+
activated in the configuration manager. To activate this setup, use the
|
|
1127
|
+
following command:
|
|
1128
|
+
|
|
1129
|
+
setup = load_setup({str(setup.get_id())})
|
|
1130
|
+
[/]
|
|
1131
|
+
"""
|
|
1132
|
+
)
|
|
1100
1133
|
)
|
|
1101
1134
|
|
|
1102
1135
|
return setup.get_id() if setup is not None else None
|
|
@@ -1104,29 +1137,18 @@ def submit_setup(setup: Setup, description: str):
|
|
|
1104
1137
|
except ConnectionError:
|
|
1105
1138
|
rich.print("Could not make a connection with the Configuration Manager, no Setup was submitted.")
|
|
1106
1139
|
except NotImplementedError:
|
|
1107
|
-
rich.print(
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
__all__ = [
|
|
1117
|
-
"Setup",
|
|
1118
|
-
"list_setups",
|
|
1119
|
-
"load_setup",
|
|
1120
|
-
"get_setup",
|
|
1121
|
-
"submit_setup",
|
|
1122
|
-
"SetupError",
|
|
1123
|
-
"load_last_setup_id",
|
|
1124
|
-
"save_last_setup_id",
|
|
1125
|
-
]
|
|
1140
|
+
rich.print(
|
|
1141
|
+
textwrap.dedent(
|
|
1142
|
+
"""\
|
|
1143
|
+
Caught a NotImplementedError. That usually means the configuration manager is not running or
|
|
1144
|
+
can not be reached. Check on the egse-server if the `cm_cs` process is running. If not you will
|
|
1145
|
+
need to be restart the core services.
|
|
1146
|
+
"""
|
|
1147
|
+
)
|
|
1148
|
+
)
|
|
1126
1149
|
|
|
1127
|
-
if __name__ == "__main__":
|
|
1128
1150
|
|
|
1129
|
-
|
|
1151
|
+
def main(args: list = None): # pragma: no cover
|
|
1130
1152
|
import argparse
|
|
1131
1153
|
|
|
1132
1154
|
from rich import print
|
|
@@ -1137,10 +1159,10 @@ if __name__ == "__main__":
|
|
|
1137
1159
|
SITE = Settings.load("SITE")
|
|
1138
1160
|
location = os.environ.get("PLATO_CONF_DATA_LOCATION")
|
|
1139
1161
|
parser = argparse.ArgumentParser(
|
|
1140
|
-
description=textwrap.dedent(
|
|
1141
|
-
Print out the Setup for the given setup-id. The Setup will
|
|
1142
|
-
be loaded from the location given by the environment variable
|
|
1143
|
-
PLATO_CONF_DATA_LOCATION. If this env is not set, the Setup
|
|
1162
|
+
description=textwrap.dedent("""\
|
|
1163
|
+
Print out the Setup for the given setup-id. The Setup will
|
|
1164
|
+
be loaded from the location given by the environment variable
|
|
1165
|
+
PLATO_CONF_DATA_LOCATION. If this env is not set, the Setup
|
|
1144
1166
|
will be searched from the current directory."""
|
|
1145
1167
|
),
|
|
1146
1168
|
epilog=f"PLATO_CONF_DATA_LOCATION={location}"
|
|
@@ -1150,17 +1172,21 @@ if __name__ == "__main__":
|
|
|
1150
1172
|
help="the Setup ID. If not given, the last Setup will be selected.")
|
|
1151
1173
|
parser.add_argument("--list", "-l", action="store_true", help="list available Setups.")
|
|
1152
1174
|
parser.add_argument("--use-cm", action="store_true", help="use the configuration manager.")
|
|
1153
|
-
args = parser.parse_args()
|
|
1175
|
+
args = parser.parse_args(args or [])
|
|
1154
1176
|
|
|
1155
1177
|
if args.use_cm:
|
|
1156
|
-
|
|
1178
|
+
try:
|
|
1179
|
+
from egse.confman import ConfigurationManagerProxy
|
|
1180
|
+
except ImportError:
|
|
1181
|
+
print("WARNING: package 'cgse-core' is not installed, service not available.")
|
|
1182
|
+
return
|
|
1157
1183
|
|
|
1158
1184
|
with ConfigurationManagerProxy() as cm:
|
|
1159
1185
|
if args.list:
|
|
1160
1186
|
print(cm.list_setups())
|
|
1161
1187
|
else:
|
|
1162
1188
|
print(cm.get_setup())
|
|
1163
|
-
|
|
1189
|
+
return
|
|
1164
1190
|
|
|
1165
1191
|
if args.list:
|
|
1166
1192
|
files = find_files(f"SETUP_{SITE.ID}_*_*.yaml", root=location)
|
|
@@ -1175,6 +1201,15 @@ if __name__ == "__main__":
|
|
|
1175
1201
|
setup_files = find_files(f"SETUP_{SITE.ID}_*_*.yaml", root=location)
|
|
1176
1202
|
else:
|
|
1177
1203
|
setup_files = find_files(f"SETUP_{SITE.ID}_{setup_id:05d}_*.yaml", root=location)
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1204
|
+
setup_files = list(setup_files)
|
|
1205
|
+
if len(setup_files) > 0:
|
|
1206
|
+
setup_file = sorted(setup_files)[-1]
|
|
1207
|
+
setup = Setup.from_yaml_file(setup_file)
|
|
1208
|
+
print(setup)
|
|
1209
|
+
else:
|
|
1210
|
+
print("[red]No setup files were found.[/]")
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
if __name__ == "__main__":
|
|
1214
|
+
import sys
|
|
1215
|
+
main(sys.argv[1:])
|
egse/system.py
CHANGED
|
@@ -29,6 +29,7 @@ import subprocess # For executing a shell command
|
|
|
29
29
|
import sys
|
|
30
30
|
import time
|
|
31
31
|
from collections import namedtuple
|
|
32
|
+
from contextlib import contextmanager
|
|
32
33
|
from pathlib import Path
|
|
33
34
|
from types import FunctionType
|
|
34
35
|
from types import ModuleType
|
|
@@ -36,7 +37,6 @@ from typing import Any
|
|
|
36
37
|
from typing import Callable
|
|
37
38
|
from typing import Iterable
|
|
38
39
|
from typing import List
|
|
39
|
-
from typing import NamedTuple
|
|
40
40
|
from typing import Optional
|
|
41
41
|
from typing import Tuple
|
|
42
42
|
from typing import Union
|
|
@@ -51,12 +51,6 @@ TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f%z"
|
|
|
51
51
|
|
|
52
52
|
logger = logging.getLogger(__name__)
|
|
53
53
|
|
|
54
|
-
from contextlib import contextmanager
|
|
55
|
-
import logging
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# Code below is copied from https://gist.github.com/simon-weber/7853144
|
|
59
|
-
|
|
60
54
|
|
|
61
55
|
@contextmanager
|
|
62
56
|
def all_logging_disabled(highest_level=logging.CRITICAL, flag=True):
|
|
@@ -68,6 +62,7 @@ def all_logging_disabled(highest_level=logging.CRITICAL, flag=True):
|
|
|
68
62
|
This would only need to be changed if a custom level greater than CRITICAL is defined.
|
|
69
63
|
flag: True to disable all logging [default=True]
|
|
70
64
|
"""
|
|
65
|
+
# Code below is copied from https://gist.github.com/simon-weber/7853144
|
|
71
66
|
# two kind-of hacks here:
|
|
72
67
|
# * can't get the highest logging level in effect => delegate to the user
|
|
73
68
|
# * can't get the current module-level override => use an undocumented
|
|
@@ -939,14 +934,19 @@ def env_var(**kwargs):
|
|
|
939
934
|
try:
|
|
940
935
|
for k, v in kwargs.items():
|
|
941
936
|
saved_env[k] = os.environ.get(k)
|
|
942
|
-
|
|
937
|
+
if v is None:
|
|
938
|
+
if k in os.environ:
|
|
939
|
+
del os.environ[k]
|
|
940
|
+
else:
|
|
941
|
+
os.environ[k] = v
|
|
943
942
|
yield
|
|
944
943
|
finally:
|
|
945
944
|
for k, v in saved_env.items():
|
|
946
|
-
if v is
|
|
947
|
-
os.environ
|
|
945
|
+
if v is None:
|
|
946
|
+
if k in os.environ:
|
|
947
|
+
del os.environ[k]
|
|
948
948
|
else:
|
|
949
|
-
|
|
949
|
+
os.environ[k] = v
|
|
950
950
|
|
|
951
951
|
|
|
952
952
|
def filter_by_attr(elements: Iterable, **attrs) -> List:
|
|
@@ -1121,6 +1121,25 @@ def is_namespace(module) -> bool:
|
|
|
1121
1121
|
|
|
1122
1122
|
|
|
1123
1123
|
def get_package_location(module) -> List[Path]:
|
|
1124
|
+
"""
|
|
1125
|
+
Retrieves the file system locations associated with a Python package.
|
|
1126
|
+
|
|
1127
|
+
This function takes a module, module name, or fully qualified module path,
|
|
1128
|
+
and returns a list of Path objects representing the file system locations
|
|
1129
|
+
associated with the package. If the module is a namespace package, it returns
|
|
1130
|
+
the paths of all namespaces; otherwise, it returns the location of the module.
|
|
1131
|
+
|
|
1132
|
+
Args:
|
|
1133
|
+
module (Union[FunctionType, ModuleType, str]): The module or module name to
|
|
1134
|
+
retrieve locations for.
|
|
1135
|
+
|
|
1136
|
+
Returns:
|
|
1137
|
+
List[Path]: A list of Path objects representing the file system locations.
|
|
1138
|
+
|
|
1139
|
+
Note:
|
|
1140
|
+
If the module is not found or is not a valid module, an empty list is returned.
|
|
1141
|
+
|
|
1142
|
+
"""
|
|
1124
1143
|
|
|
1125
1144
|
if isinstance(module, FunctionType):
|
|
1126
1145
|
module_name = module.__module__
|
|
@@ -1133,7 +1152,7 @@ def get_package_location(module) -> List[Path]:
|
|
|
1133
1152
|
|
|
1134
1153
|
try:
|
|
1135
1154
|
module = importlib.import_module(module)
|
|
1136
|
-
except TypeError
|
|
1155
|
+
except TypeError:
|
|
1137
1156
|
return []
|
|
1138
1157
|
|
|
1139
1158
|
if is_namespace(module):
|
|
@@ -1180,7 +1199,7 @@ def get_module_location(arg) -> Optional[Path]:
|
|
|
1180
1199
|
|
|
1181
1200
|
try:
|
|
1182
1201
|
module = importlib.import_module(module_name)
|
|
1183
|
-
except TypeError
|
|
1202
|
+
except TypeError:
|
|
1184
1203
|
return None
|
|
1185
1204
|
|
|
1186
1205
|
if is_namespace(module):
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
egse/bits.py,sha256=6oNKp6BrCjwMlZ6lDwMPuZXTT6NzRW4Y04jLd5ALUGc,10235
|
|
2
|
-
egse/command.py,sha256=PEPivQQu_FPerhEveVri_qiUkFnrjFA2nIkgo9qwXNg,23112
|
|
3
|
-
egse/config.py,sha256=xQL3vdheYywBKzX70hoT4He9l29GlTI24CQfdtwMn-U,14743
|
|
4
|
-
egse/control.py,sha256=TzaUDAHOttKRmnJvWbNLAQmNatWtZ_ExDJihqf0QTIE,15193
|
|
5
|
-
egse/decorators.py,sha256=yR7QrXrdCxcyL5dnjD-gCI0eMGq43lo3Xp8GABtHYVU,13452
|
|
6
|
-
egse/device.py,sha256=NGsjVELxMFFKJd1w-Bs1dFsAIpeistdv1H5xARdj1O4,8385
|
|
7
|
-
egse/env.py,sha256=xTl5q1tUY0YtJMVUraAmU9wnziSIN1gzdO6cMYkjqfg,10306
|
|
8
|
-
egse/exceptions.py,sha256=Tc1xqUrWxV6SXxc9xB9viK_O-GVa_SpzqZUVEhZkwzA,1801
|
|
9
|
-
egse/mixin.py,sha256=J3yu5lPnm0grJqIm5LiacBUCZujJsdcKBNRaOQcMnNo,17345
|
|
10
|
-
egse/monitoring.py,sha256=-pwXqPoiNKzQYKQSpKddFlaPkCTJZYdxvG1d2MBN3l0,3033
|
|
11
|
-
egse/observer.py,sha256=6faaLHqgpOQs_oEvdBygQ5HF7mGneKJEfyQEFUFA5VY,1069
|
|
12
|
-
egse/obsid.py,sha256=-HPuHApZrr3Nj1J2-qqnIiE814C-gm4FSHdM2afKdRY,5883
|
|
13
|
-
egse/persistence.py,sha256=Lx6LMJ1-dh8N43XF7tTM6RwD0sSETiGQ9DNqug-G-zQ,2160
|
|
14
|
-
egse/plugin.py,sha256=Fd_QnABm33_dLjaj1zDgEZr3RKy-N88U5Hz5OZm9Jj0,2684
|
|
15
|
-
egse/process.py,sha256=mQ2ojeL_9oE_QkMJlQDPd1290z0j2mOrGXrlrWtOtzI,16615
|
|
16
|
-
egse/protocol.py,sha256=Psy0iOLPTgARn1VqeKtPCSKepHr_S2KW58UYwjOA6J0,23827
|
|
17
|
-
egse/proxy.py,sha256=pMKdnF62SXm0quLoKfgvK9GFkH2mLMB5fWNrZenfqQQ,18100
|
|
18
|
-
egse/reload.py,sha256=rDT0bC6RFeRhW38wSgFcxr30h8FvaKkoGp_OE-AwBB4,4388
|
|
19
|
-
egse/resource.py,sha256=FV6KYRnWZL0-xbrpAYEDHV0bPhvhAJgJmrKwOBfMWSk,12658
|
|
20
|
-
egse/services.py,sha256=ZgkF0Rx_PykOVHAOV1cKduJdUhuY6A4DgwjPJWRGj3U,7642
|
|
21
|
-
egse/services.yaml,sha256=p8QBF56zLI21iJ9skt65VlNz4rIqRoFfBTZxOIUZCZ4,1853
|
|
22
|
-
egse/settings.py,sha256=t0Zzb1W40fF6cnJ2xvOdt0ZYnoE8i22wdS_co9SbTOI,13389
|
|
23
|
-
egse/settings.yaml,sha256=g0xhRWWGwysmYUIIz4ViDG78TeDj230tZF48CA7Gg4g,48652
|
|
24
|
-
egse/setup.py,sha256=XZN5vPf7kcgkIVP2c2gE3DXhJQApIRoeKndXVhZwpS4,41676
|
|
25
|
-
egse/state.py,sha256=ekcCZu_DZKkKYn-5iWG7ij7Aif2WYMNVs5h3cia-cVc,5352
|
|
26
|
-
egse/system.py,sha256=yvADB0ukpKFEd64tfx50TpG4y22UrnXOwqbko9UK2SY,48100
|
|
27
|
-
egse/version.py,sha256=EigdH05E8pNtSQznUfqM_RxlOjuAVp3Oe6S6MM5xGIM,6132
|
|
28
|
-
egse/zmq_ser.py,sha256=2-nwVUBWZ3vvosKNmlWobHJrIJA2HlM3V5a63Gz2JY0,1819
|
|
29
|
-
cgse_common-2023.1.5.dist-info/METADATA,sha256=XJquyxu7_-dlJz0RpUVfZnibGJcOT08zDd3yf5Nt4Sc,2321
|
|
30
|
-
cgse_common-2023.1.5.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
|
31
|
-
cgse_common-2023.1.5.dist-info/entry_points.txt,sha256=wl2zf4c5s9gQ-LAYVyiBtqTAZFxx8zuOZ2J5xz-r9zw,33
|
|
32
|
-
cgse_common-2023.1.5.dist-info/RECORD,,
|