DIRAC 9.0.0a54__py3-none-any.whl → 9.0.7__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.
- DIRAC/AccountingSystem/Client/AccountingCLI.py +0 -140
- DIRAC/AccountingSystem/Client/DataStoreClient.py +0 -13
- DIRAC/AccountingSystem/Client/Types/BaseAccountingType.py +0 -7
- DIRAC/AccountingSystem/ConfigTemplate.cfg +0 -5
- DIRAC/AccountingSystem/Service/DataStoreHandler.py +0 -72
- DIRAC/ConfigurationSystem/Client/Helpers/CSGlobals.py +0 -9
- DIRAC/ConfigurationSystem/Client/Helpers/Registry.py +34 -32
- DIRAC/ConfigurationSystem/Client/Helpers/Resources.py +11 -43
- DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py +0 -16
- DIRAC/ConfigurationSystem/Client/LocalConfiguration.py +14 -8
- DIRAC/ConfigurationSystem/Client/PathFinder.py +47 -8
- DIRAC/ConfigurationSystem/Client/SyncPlugins/CERNLDAPSyncPlugin.py +4 -1
- DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py +9 -2
- DIRAC/ConfigurationSystem/Client/test/Test_PathFinder.py +41 -1
- DIRAC/ConfigurationSystem/private/RefresherBase.py +4 -2
- DIRAC/Core/DISET/ServiceReactor.py +11 -3
- DIRAC/Core/DISET/private/BaseClient.py +1 -2
- DIRAC/Core/DISET/private/Transports/M2SSLTransport.py +9 -7
- DIRAC/Core/Security/DiracX.py +12 -7
- DIRAC/Core/Security/IAMService.py +4 -3
- DIRAC/Core/Security/ProxyInfo.py +9 -5
- DIRAC/Core/Security/test/test_diracx_token_from_pem.py +161 -0
- DIRAC/Core/Tornado/Client/ClientSelector.py +4 -1
- DIRAC/Core/Tornado/Server/TornadoService.py +1 -1
- DIRAC/Core/Utilities/ClassAd/ClassAdLight.py +4 -290
- DIRAC/Core/Utilities/DErrno.py +5 -309
- DIRAC/Core/Utilities/Extensions.py +10 -1
- DIRAC/Core/Utilities/Graphs/GraphData.py +1 -1
- DIRAC/Core/Utilities/JDL.py +1 -195
- DIRAC/Core/Utilities/List.py +1 -124
- DIRAC/Core/Utilities/MySQL.py +101 -97
- DIRAC/Core/Utilities/Os.py +32 -1
- DIRAC/Core/Utilities/Platform.py +2 -107
- DIRAC/Core/Utilities/ReturnValues.py +7 -252
- DIRAC/Core/Utilities/StateMachine.py +12 -178
- DIRAC/Core/Utilities/TimeUtilities.py +10 -253
- DIRAC/Core/Utilities/test/Test_JDL.py +0 -3
- DIRAC/Core/Utilities/test/Test_Profiler.py +20 -20
- DIRAC/Core/scripts/dirac_agent.py +1 -1
- DIRAC/Core/scripts/dirac_apptainer_exec.py +16 -7
- DIRAC/Core/scripts/dirac_platform.py +1 -92
- DIRAC/DataManagementSystem/Agent/FTS3Agent.py +8 -7
- DIRAC/DataManagementSystem/Agent/RequestOperations/RemoveFile.py +7 -6
- DIRAC/DataManagementSystem/Client/FTS3Job.py +71 -34
- DIRAC/DataManagementSystem/DB/FTS3DB.py +3 -0
- DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py +1 -1
- DIRAC/DataManagementSystem/Utilities/DMSHelpers.py +6 -2
- DIRAC/DataManagementSystem/scripts/dirac_dms_create_moving_request.py +2 -0
- DIRAC/DataManagementSystem/scripts/dirac_dms_protocol_matrix.py +0 -1
- DIRAC/FrameworkSystem/Client/ComponentInstaller.py +4 -2
- DIRAC/FrameworkSystem/DB/ProxyDB.py +9 -5
- DIRAC/FrameworkSystem/Utilities/TokenManagementUtilities.py +3 -2
- DIRAC/FrameworkSystem/Utilities/diracx.py +2 -74
- DIRAC/FrameworkSystem/private/authorization/AuthServer.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_login.py +2 -2
- DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py +1 -1
- DIRAC/Interfaces/API/Dirac.py +27 -13
- DIRAC/Interfaces/API/DiracAdmin.py +42 -7
- DIRAC/Interfaces/API/Job.py +1 -0
- DIRAC/Interfaces/scripts/dirac_admin_allow_site.py +7 -1
- DIRAC/Interfaces/scripts/dirac_admin_ban_site.py +7 -1
- DIRAC/Interfaces/scripts/dirac_wms_job_parameters.py +0 -1
- DIRAC/MonitoringSystem/Client/Types/WMSHistory.py +4 -0
- DIRAC/MonitoringSystem/Client/WebAppClient.py +26 -0
- DIRAC/MonitoringSystem/ConfigTemplate.cfg +9 -0
- DIRAC/MonitoringSystem/DB/MonitoringDB.py +6 -25
- DIRAC/MonitoringSystem/Service/MonitoringHandler.py +0 -33
- DIRAC/MonitoringSystem/Service/WebAppHandler.py +599 -0
- DIRAC/MonitoringSystem/private/MainReporter.py +0 -3
- DIRAC/ProductionSystem/scripts/dirac_prod_get_trans.py +2 -3
- DIRAC/RequestManagementSystem/Agent/RequestExecutingAgent.py +8 -6
- DIRAC/RequestManagementSystem/ConfigTemplate.cfg +6 -6
- DIRAC/RequestManagementSystem/DB/test/RMSTestScenari.py +2 -0
- DIRAC/ResourceStatusSystem/Client/SiteStatus.py +4 -2
- DIRAC/ResourceStatusSystem/Command/FreeDiskSpaceCommand.py +3 -1
- DIRAC/ResourceStatusSystem/Utilities/CSHelpers.py +2 -31
- DIRAC/ResourceStatusSystem/scripts/dirac_rss_set_status.py +18 -4
- DIRAC/Resources/Catalog/RucioFileCatalogClient.py +1 -1
- DIRAC/Resources/Computing/AREXComputingElement.py +19 -3
- DIRAC/Resources/Computing/BatchSystems/Condor.py +126 -108
- DIRAC/Resources/Computing/BatchSystems/SLURM.py +5 -1
- DIRAC/Resources/Computing/BatchSystems/test/Test_SLURM.py +46 -0
- DIRAC/Resources/Computing/HTCondorCEComputingElement.py +37 -43
- DIRAC/Resources/Computing/SingularityComputingElement.py +6 -1
- DIRAC/Resources/Computing/test/Test_HTCondorCEComputingElement.py +67 -49
- DIRAC/Resources/Computing/test/Test_PoolComputingElement.py +2 -1
- DIRAC/Resources/IdProvider/CheckInIdProvider.py +13 -0
- DIRAC/Resources/IdProvider/IdProviderFactory.py +11 -3
- DIRAC/Resources/Storage/StorageBase.py +4 -2
- DIRAC/Resources/Storage/StorageElement.py +4 -4
- DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +10 -16
- DIRAC/TransformationSystem/Agent/TransformationAgent.py +22 -1
- DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +15 -15
- DIRAC/TransformationSystem/Client/Transformation.py +2 -1
- DIRAC/TransformationSystem/Client/TransformationClient.py +0 -7
- DIRAC/TransformationSystem/Client/Utilities.py +9 -0
- DIRAC/TransformationSystem/Service/TransformationManagerHandler.py +0 -336
- DIRAC/TransformationSystem/Utilities/ReplicationCLIParameters.py +3 -3
- DIRAC/TransformationSystem/scripts/dirac_production_runjoblocal.py +2 -4
- DIRAC/TransformationSystem/test/Test_replicationTransformation.py +5 -6
- DIRAC/Workflow/Modules/test/Test_Modules.py +5 -0
- DIRAC/WorkloadManagementSystem/Agent/JobAgent.py +1 -5
- DIRAC/WorkloadManagementSystem/Agent/JobCleaningAgent.py +11 -7
- DIRAC/WorkloadManagementSystem/Agent/PilotSyncAgent.py +4 -3
- DIRAC/WorkloadManagementSystem/Agent/PushJobAgent.py +13 -13
- DIRAC/WorkloadManagementSystem/Agent/SiteDirector.py +10 -13
- DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +18 -51
- DIRAC/WorkloadManagementSystem/Agent/StatesAccountingAgent.py +41 -1
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobAgent.py +2 -0
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_JobCleaningAgent.py +7 -9
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_PushJobAgent.py +1 -0
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_SiteDirector.py +8 -2
- DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +4 -5
- DIRAC/WorkloadManagementSystem/Client/DownloadInputData.py +7 -5
- DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py +10 -11
- DIRAC/WorkloadManagementSystem/Client/JobState/JobManifest.py +32 -261
- DIRAC/WorkloadManagementSystem/Client/JobStateUpdateClient.py +3 -0
- DIRAC/WorkloadManagementSystem/Client/JobStatus.py +8 -152
- DIRAC/WorkloadManagementSystem/Client/SandboxStoreClient.py +25 -38
- DIRAC/WorkloadManagementSystem/Client/WMSClient.py +2 -3
- DIRAC/WorkloadManagementSystem/Client/test/Test_Client_DownloadInputData.py +29 -0
- DIRAC/WorkloadManagementSystem/ConfigTemplate.cfg +4 -8
- DIRAC/WorkloadManagementSystem/DB/JobDB.py +40 -69
- DIRAC/WorkloadManagementSystem/DB/JobDBUtils.py +18 -147
- DIRAC/WorkloadManagementSystem/DB/JobParametersDB.py +9 -9
- DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py +3 -2
- DIRAC/WorkloadManagementSystem/DB/SandboxMetadataDB.py +28 -39
- DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +125 -0
- DIRAC/WorkloadManagementSystem/DB/tests/Test_JobDB.py +1 -1
- DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +28 -0
- DIRAC/WorkloadManagementSystem/Executor/JobSanity.py +3 -3
- DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py +2 -14
- DIRAC/WorkloadManagementSystem/JobWrapper/JobWrapper.py +14 -9
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapper.py +36 -10
- DIRAC/WorkloadManagementSystem/JobWrapper/test/Test_JobWrapperTemplate.py +4 -0
- DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +33 -154
- DIRAC/WorkloadManagementSystem/Service/JobMonitoringHandler.py +5 -323
- DIRAC/WorkloadManagementSystem/Service/JobStateUpdateHandler.py +0 -16
- DIRAC/WorkloadManagementSystem/Service/PilotManagerHandler.py +6 -102
- DIRAC/WorkloadManagementSystem/Service/SandboxStoreHandler.py +5 -51
- DIRAC/WorkloadManagementSystem/Service/WMSAdministratorHandler.py +16 -79
- DIRAC/WorkloadManagementSystem/Utilities/JobModel.py +28 -199
- DIRAC/WorkloadManagementSystem/Utilities/JobParameters.py +65 -3
- DIRAC/WorkloadManagementSystem/Utilities/JobStatusUtility.py +2 -64
- DIRAC/WorkloadManagementSystem/Utilities/ParametricJob.py +7 -171
- DIRAC/WorkloadManagementSystem/Utilities/PilotCStoJSONSynchronizer.py +73 -7
- DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py +2 -0
- DIRAC/WorkloadManagementSystem/Utilities/RemoteRunner.py +16 -0
- DIRAC/WorkloadManagementSystem/Utilities/Utils.py +36 -1
- DIRAC/WorkloadManagementSystem/Utilities/jobAdministration.py +15 -0
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobModel.py +1 -5
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_ParametricJob.py +45 -128
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_PilotWrapper.py +16 -0
- DIRAC/__init__.py +55 -54
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/METADATA +6 -4
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/RECORD +160 -160
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/WHEEL +1 -1
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/entry_points.txt +0 -3
- DIRAC/Core/Utilities/test/Test_List.py +0 -150
- DIRAC/Core/Utilities/test/Test_Time.py +0 -88
- DIRAC/TransformationSystem/scripts/dirac_transformation_archive.py +0 -30
- DIRAC/TransformationSystem/scripts/dirac_transformation_clean.py +0 -30
- DIRAC/TransformationSystem/scripts/dirac_transformation_remove_output.py +0 -30
- DIRAC/WorkloadManagementSystem/Utilities/test/Test_JobManager.py +0 -58
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/licenses/LICENSE +0 -0
- {dirac-9.0.0a54.dist-info → dirac-9.0.7.dist-info}/top_level.txt +0 -0
DIRAC/Core/Utilities/Platform.py
CHANGED
|
@@ -2,119 +2,14 @@
|
|
|
2
2
|
Compile the externals
|
|
3
3
|
"""
|
|
4
4
|
import platform
|
|
5
|
-
import sys
|
|
6
|
-
import os
|
|
7
|
-
import re
|
|
8
|
-
|
|
9
|
-
# We need to patch python platform module. It does a string comparison for the libc versions.
|
|
10
|
-
# it fails when going from 2.9 to 2.10,
|
|
11
|
-
# the fix converts the version to a tuple and attempts a numeric comparison
|
|
12
|
-
|
|
13
|
-
_libc_search = re.compile(b"(__libc_init)" b"|" b"(GLIBC_([0-9.]+))" b"|" b"(libc(_\\w+)?\\.so(?:\\.(\\d[0-9.]*))?)")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def libc_ver(executable=sys.executable, lib="", version="", chunksize=2048):
|
|
17
|
-
"""Tries to determine the libc version that the file executable
|
|
18
|
-
(which defaults to the Python interpreter) is linked against.
|
|
19
|
-
|
|
20
|
-
Returns a tuple of strings (lib,version) which default to the
|
|
21
|
-
given parameters in case the lookup fails.
|
|
22
|
-
|
|
23
|
-
Note that the function has intimate knowledge of how different
|
|
24
|
-
libc versions add symbols to the executable and thus is probably
|
|
25
|
-
only useable for executables compiled using gcc.
|
|
26
|
-
|
|
27
|
-
The file is read and scanned in chunks of chunksize bytes.
|
|
28
|
-
|
|
29
|
-
"""
|
|
30
|
-
with open(executable, "rb") as f:
|
|
31
|
-
binary = f.read(chunksize)
|
|
32
|
-
pos = 0
|
|
33
|
-
version = [0, 0, 0]
|
|
34
|
-
while True:
|
|
35
|
-
m = _libc_search.search(binary, pos)
|
|
36
|
-
if not m:
|
|
37
|
-
binary = f.read(chunksize)
|
|
38
|
-
if not binary:
|
|
39
|
-
break
|
|
40
|
-
pos = 0
|
|
41
|
-
continue
|
|
42
|
-
libcinit, glibc, glibcversion, so, threads, soversion = m.groups()
|
|
43
|
-
if libcinit and not lib:
|
|
44
|
-
lib = b"libc"
|
|
45
|
-
elif glibc:
|
|
46
|
-
glibcversion_parts = glibcversion.split(b".")
|
|
47
|
-
for i, part in enumerate(glibcversion_parts):
|
|
48
|
-
try:
|
|
49
|
-
glibcversion_parts[i] = int(part)
|
|
50
|
-
except ValueError:
|
|
51
|
-
glibcversion_parts[i] = 0
|
|
52
|
-
if libcinit and not lib:
|
|
53
|
-
lib = b"libc"
|
|
54
|
-
elif glibc:
|
|
55
|
-
if lib != b"glibc":
|
|
56
|
-
lib = b"glibc"
|
|
57
|
-
version = glibcversion_parts
|
|
58
|
-
elif glibcversion_parts > version:
|
|
59
|
-
version = glibcversion_parts
|
|
60
|
-
elif so:
|
|
61
|
-
if lib != b"glibc":
|
|
62
|
-
lib = b"libc"
|
|
63
|
-
version = max(version, [int(soversion)]).pop()
|
|
64
|
-
if threads and version[-len(threads) :] != threads:
|
|
65
|
-
version = version + threads
|
|
66
|
-
pos = m.end()
|
|
67
|
-
return lib.decode(), ".".join(map(str, version))
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# ## Command line interface
|
|
71
5
|
|
|
72
6
|
|
|
7
|
+
# Command line interface
|
|
73
8
|
def getPlatformString():
|
|
74
9
|
# Modified to return our desired platform string, R. Graciani
|
|
75
10
|
platformTuple = (platform.system(), platform.machine())
|
|
76
11
|
if platformTuple[0] == "Linux":
|
|
77
|
-
|
|
78
|
-
import subprocess
|
|
79
|
-
|
|
80
|
-
sp = subprocess.Popen(["/sbin/ldconfig", "--print-cache"], stdout=subprocess.PIPE, universal_newlines=True)
|
|
81
|
-
spStdout = sp.stdout
|
|
82
|
-
except Exception:
|
|
83
|
-
sp = None
|
|
84
|
-
spStdout = os.popen("/sbin/ldconfig --print-cache", "r")
|
|
85
|
-
ldre = re.compile(r".*=> (.*/libc\.so\..*$)")
|
|
86
|
-
libs = []
|
|
87
|
-
for line in spStdout.readlines():
|
|
88
|
-
reM = ldre.match(line)
|
|
89
|
-
if reM:
|
|
90
|
-
libs.append(reM.groups()[0])
|
|
91
|
-
if sp:
|
|
92
|
-
if "terminate" in dir(sp):
|
|
93
|
-
sp.terminate()
|
|
94
|
-
sp.wait()
|
|
95
|
-
|
|
96
|
-
if not libs:
|
|
97
|
-
# get version of higher libc installed
|
|
98
|
-
if platform.machine().find("64") != -1:
|
|
99
|
-
lib = "/lib64"
|
|
100
|
-
else:
|
|
101
|
-
lib = "/lib"
|
|
102
|
-
for libFile in os.listdir(lib):
|
|
103
|
-
if libFile.find("libc-") == 0 or libFile.find("libc.so") == 0:
|
|
104
|
-
libs.append(os.path.join(lib, libFile))
|
|
105
|
-
newest_lib = [0, 0, 0]
|
|
106
|
-
for lib in libs:
|
|
107
|
-
lib_parts = libc_ver(lib)[1].split(".")
|
|
108
|
-
for i, part in enumerate(lib_parts):
|
|
109
|
-
try:
|
|
110
|
-
lib_parts[i] = int(part)
|
|
111
|
-
except ValueError:
|
|
112
|
-
lib_parts[i] = 0
|
|
113
|
-
# print "non integer version numbers"
|
|
114
|
-
if lib_parts > newest_lib:
|
|
115
|
-
newest_lib = lib_parts
|
|
116
|
-
|
|
117
|
-
platformTuple += ("glibc-" + ".".join(map(str, newest_lib)),)
|
|
12
|
+
platformTuple += ("-".join(platform.libc_ver()),)
|
|
118
13
|
elif platformTuple[0] == "Darwin":
|
|
119
14
|
platformTuple += (".".join(platform.mac_ver()[0].split(".")[:2]),)
|
|
120
15
|
else:
|
|
@@ -1,255 +1,10 @@
|
|
|
1
|
-
"""
|
|
2
|
-
DIRAC return dictionary
|
|
1
|
+
"""Backward compatibility wrapper - moved to DIRACCommon
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
This module has been moved to DIRACCommon.Core.Utilities.ReturnValues to avoid
|
|
4
|
+
circular dependencies and allow DiracX to use these utilities without
|
|
5
|
+
triggering DIRAC's global state initialization.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
All exports are maintained for backward compatibility.
|
|
7
8
|
"""
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
import functools
|
|
11
|
-
import sys
|
|
12
|
-
import traceback
|
|
13
|
-
from types import TracebackType
|
|
14
|
-
from typing import Any, Callable, cast, Generic, Literal, overload, Type, TypeVar, Union
|
|
15
|
-
from typing_extensions import TypedDict, ParamSpec, NotRequired
|
|
16
|
-
|
|
17
|
-
from DIRAC.Core.Utilities.DErrno import strerror
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
T = TypeVar("T")
|
|
21
|
-
P = ParamSpec("P")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class DOKReturnType(TypedDict, Generic[T]):
|
|
25
|
-
"""used for typing the DIRAC return structure"""
|
|
26
|
-
|
|
27
|
-
OK: Literal[True]
|
|
28
|
-
Value: T
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class DErrorReturnType(TypedDict):
|
|
32
|
-
"""used for typing the DIRAC return structure"""
|
|
33
|
-
|
|
34
|
-
OK: Literal[False]
|
|
35
|
-
Message: str
|
|
36
|
-
Errno: int
|
|
37
|
-
ExecInfo: NotRequired[tuple[type[BaseException], BaseException, TracebackType]]
|
|
38
|
-
CallStack: NotRequired[list[str]]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
DReturnType = Union[DOKReturnType[T], DErrorReturnType]
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def S_ERROR(*args: Any, **kwargs: Any) -> DErrorReturnType:
|
|
45
|
-
"""return value on error condition
|
|
46
|
-
|
|
47
|
-
Arguments are either Errno and ErrorMessage or just ErrorMessage fro backward compatibility
|
|
48
|
-
|
|
49
|
-
:param int errno: Error number
|
|
50
|
-
:param string message: Error message
|
|
51
|
-
:param list callStack: Manually override the CallStack attribute better performance
|
|
52
|
-
"""
|
|
53
|
-
callStack = kwargs.pop("callStack", None)
|
|
54
|
-
|
|
55
|
-
result: DErrorReturnType = {"OK": False, "Errno": 0, "Message": ""}
|
|
56
|
-
|
|
57
|
-
message = ""
|
|
58
|
-
if args:
|
|
59
|
-
if isinstance(args[0], int):
|
|
60
|
-
result["Errno"] = args[0]
|
|
61
|
-
if len(args) > 1:
|
|
62
|
-
message = args[1]
|
|
63
|
-
else:
|
|
64
|
-
message = args[0]
|
|
65
|
-
|
|
66
|
-
if result["Errno"]:
|
|
67
|
-
message = f"{strerror(result['Errno'])} ( {result['Errno']} : {message})"
|
|
68
|
-
result["Message"] = message
|
|
69
|
-
|
|
70
|
-
if callStack is None:
|
|
71
|
-
try:
|
|
72
|
-
callStack = traceback.format_stack()
|
|
73
|
-
callStack.pop()
|
|
74
|
-
except Exception:
|
|
75
|
-
callStack = []
|
|
76
|
-
|
|
77
|
-
result["CallStack"] = callStack
|
|
78
|
-
|
|
79
|
-
return result
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# mypy doesn't understand default parameter values with generics so use overloads (python/mypy#3737)
|
|
83
|
-
@overload
|
|
84
|
-
def S_OK() -> DOKReturnType[None]:
|
|
85
|
-
...
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@overload
|
|
89
|
-
def S_OK(value: T) -> DOKReturnType[T]:
|
|
90
|
-
...
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def S_OK(value=None): # type: ignore
|
|
94
|
-
"""return value on success
|
|
95
|
-
|
|
96
|
-
:param value: value of the 'Value'
|
|
97
|
-
:return: dictionary { 'OK' : True, 'Value' : value }
|
|
98
|
-
"""
|
|
99
|
-
return {"OK": True, "Value": value}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def isReturnStructure(unk: Any) -> bool:
|
|
103
|
-
"""Check if value is an `S_OK`/`S_ERROR` object"""
|
|
104
|
-
if not isinstance(unk, dict):
|
|
105
|
-
return False
|
|
106
|
-
if "OK" not in unk:
|
|
107
|
-
return False
|
|
108
|
-
if unk["OK"]:
|
|
109
|
-
return "Value" in unk
|
|
110
|
-
else:
|
|
111
|
-
return "Message" in unk
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def isSError(value: Any) -> bool:
|
|
115
|
-
"""Check if value is an `S_ERROR` object"""
|
|
116
|
-
if not isinstance(value, dict):
|
|
117
|
-
return False
|
|
118
|
-
if "OK" not in value:
|
|
119
|
-
return False
|
|
120
|
-
return "Message" in value
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def reprReturnErrorStructure(struct: DErrorReturnType, full: bool = False) -> str:
|
|
124
|
-
errorNumber = struct.get("Errno", 0)
|
|
125
|
-
message = struct.get("Message", "")
|
|
126
|
-
if errorNumber:
|
|
127
|
-
reprStr = f"{strerror(errorNumber)} ( {errorNumber} : {message})"
|
|
128
|
-
else:
|
|
129
|
-
reprStr = message
|
|
130
|
-
|
|
131
|
-
if full:
|
|
132
|
-
callStack = struct.get("CallStack")
|
|
133
|
-
if callStack:
|
|
134
|
-
reprStr += "\n" + "".join(callStack)
|
|
135
|
-
|
|
136
|
-
return reprStr
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def returnSingleResult(dictRes: DReturnType[Any]) -> DReturnType[Any]:
|
|
140
|
-
"""Transform the S_OK{Successful/Failed} dictionary convention into
|
|
141
|
-
an S_OK/S_ERROR return. To be used when a single returned entity
|
|
142
|
-
is expected from a generally bulk call.
|
|
143
|
-
|
|
144
|
-
:param dictRes: S_ERROR or S_OK( "Failed" : {}, "Successful" : {})
|
|
145
|
-
:returns: S_ERROR or S_OK(value)
|
|
146
|
-
|
|
147
|
-
The following rules are applied:
|
|
148
|
-
|
|
149
|
-
- if dictRes is an S_ERROR: returns it as is
|
|
150
|
-
- we start by looking at the Failed directory
|
|
151
|
-
- if there are several items in a dictionary, we return the first one
|
|
152
|
-
- if both dictionaries are empty, we return S_ERROR
|
|
153
|
-
- For an item in Failed, we return S_ERROR
|
|
154
|
-
- Far an item in Successful we return S_OK
|
|
155
|
-
|
|
156
|
-
Behavior examples (would be perfect unit test :-) )::
|
|
157
|
-
|
|
158
|
-
{'Message': 'Kaput', 'OK': False} -> {'Message': 'Kaput', 'OK': False}
|
|
159
|
-
{'OK': True, 'Value': {'Successful': {}, 'Failed': {'a': 1}}} -> {'Message': '1', 'OK': False}
|
|
160
|
-
{'OK': True, 'Value': {'Successful': {'b': 2}, 'Failed': {}}} -> {'OK': True, 'Value': 2}
|
|
161
|
-
{'OK': True, 'Value': {'Successful': {'b': 2}, 'Failed': {'a': 1}}} -> {'Message': '1', 'OK': False}
|
|
162
|
-
{'OK': True, 'Value': {'Successful': {'b': 2}, 'Failed': {'a': 1, 'c': 3}}} -> {'Message': '1', 'OK': False}
|
|
163
|
-
{'OK': True, 'Value': {'Successful': {'b': 2, 'd': 4}, 'Failed': {}}} -> {'OK': True, 'Value': 2}
|
|
164
|
-
{'OK': True, 'Value': {'Successful': {}, 'Failed': {}}} ->
|
|
165
|
-
{'Message': 'returnSingleResult: Failed and Successful dictionaries are empty', 'OK': False}
|
|
166
|
-
"""
|
|
167
|
-
# if S_ERROR was returned, we return it as well
|
|
168
|
-
if not dictRes["OK"]:
|
|
169
|
-
return dictRes
|
|
170
|
-
# if there is a Failed, we return the first one in an S_ERROR
|
|
171
|
-
if "Failed" in dictRes["Value"] and len(dictRes["Value"]["Failed"]):
|
|
172
|
-
errorMessage = list(dictRes["Value"]["Failed"].values())[0]
|
|
173
|
-
if isinstance(errorMessage, dict):
|
|
174
|
-
if isReturnStructure(errorMessage):
|
|
175
|
-
return cast(DErrorReturnType, errorMessage)
|
|
176
|
-
else:
|
|
177
|
-
return S_ERROR(str(errorMessage))
|
|
178
|
-
return S_ERROR(errorMessage)
|
|
179
|
-
# if there is a Successful, we return the first one in an S_OK
|
|
180
|
-
elif "Successful" in dictRes["Value"] and len(dictRes["Value"]["Successful"]):
|
|
181
|
-
return S_OK(list(dictRes["Value"]["Successful"].values())[0])
|
|
182
|
-
else:
|
|
183
|
-
return S_ERROR("returnSingleResult: Failed and Successful dictionaries are empty")
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
class SErrorException(Exception):
|
|
187
|
-
"""Exception class for use with `convertToReturnValue`"""
|
|
188
|
-
|
|
189
|
-
def __init__(self, result: DErrorReturnType | str, errCode: int = 0):
|
|
190
|
-
"""Create a new exception return value
|
|
191
|
-
|
|
192
|
-
If `result` is a `S_ERROR` return it directly else convert it to an
|
|
193
|
-
appropriate value using `S_ERROR(errCode, result)`.
|
|
194
|
-
|
|
195
|
-
:param result: The error to propagate
|
|
196
|
-
:param errCode: the error code to propagate
|
|
197
|
-
"""
|
|
198
|
-
if not isSError(result):
|
|
199
|
-
result = S_ERROR(errCode, result)
|
|
200
|
-
self.result = cast(DErrorReturnType, result)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
def returnValueOrRaise(result: DReturnType[T], *, errorCode: int = 0) -> T:
|
|
204
|
-
"""Unwrap an S_OK/S_ERROR response into a value or Exception
|
|
205
|
-
|
|
206
|
-
This method assists with using exceptions in DIRAC code by raising
|
|
207
|
-
:exc:`SErrorException` if `result` is an error. This can then by propagated
|
|
208
|
-
automatically as an `S_ERROR` by wrapping public facing functions with
|
|
209
|
-
`@convertToReturnValue`.
|
|
210
|
-
|
|
211
|
-
:param result: Result of a DIRAC function which returns `S_OK`/`S_ERROR`
|
|
212
|
-
:returns: The value associated with the `S_OK` object
|
|
213
|
-
:raises: If `result["OK"]` is falsey the original exception is re-raised.
|
|
214
|
-
If no exception is known an :exc:`SErrorException` is raised.
|
|
215
|
-
"""
|
|
216
|
-
if not result["OK"]:
|
|
217
|
-
if "ExecInfo" in result:
|
|
218
|
-
raise result["ExecInfo"][0]
|
|
219
|
-
else:
|
|
220
|
-
raise SErrorException(result, errorCode)
|
|
221
|
-
return result["Value"]
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def convertToReturnValue(func: Callable[P, T]) -> Callable[P, DReturnType[T]]:
|
|
225
|
-
"""Decorate a function to convert return values to `S_OK`/`S_ERROR`
|
|
226
|
-
|
|
227
|
-
If `func` returns, wrap the return value in `S_OK`.
|
|
228
|
-
If `func` raises :exc:`SErrorException`, return the associated `S_ERROR`
|
|
229
|
-
If `func` raises any other exception type, convert it to an `S_ERROR` object
|
|
230
|
-
|
|
231
|
-
:param result: The bare result of a function call
|
|
232
|
-
:returns: `S_OK`/`S_ERROR`
|
|
233
|
-
"""
|
|
234
|
-
|
|
235
|
-
@functools.wraps(func)
|
|
236
|
-
def wrapped(*args: P.args, **kwargs: P.kwargs) -> DReturnType[T]:
|
|
237
|
-
try:
|
|
238
|
-
value = func(*args, **kwargs)
|
|
239
|
-
except SErrorException as e:
|
|
240
|
-
return e.result
|
|
241
|
-
except Exception as e:
|
|
242
|
-
retval = S_ERROR(f"{repr(e)}: {e}")
|
|
243
|
-
# Replace CallStack with the one from the exception
|
|
244
|
-
# Use cast as mypy doesn't understand that sys.exc_info can't return None in an exception block
|
|
245
|
-
retval["ExecInfo"] = cast(tuple[type[BaseException], BaseException, TracebackType], sys.exc_info())
|
|
246
|
-
exc_type, exc_value, exc_tb = retval["ExecInfo"]
|
|
247
|
-
retval["CallStack"] = traceback.format_tb(exc_tb)
|
|
248
|
-
return retval
|
|
249
|
-
else:
|
|
250
|
-
return S_OK(value)
|
|
251
|
-
|
|
252
|
-
# functools will copy the annotations. Since we change the return type
|
|
253
|
-
# we have to update it
|
|
254
|
-
wrapped.__annotations__["return"] = DReturnType
|
|
255
|
-
return wrapped
|
|
9
|
+
# Re-export everything from DIRACCommon for backward compatibility
|
|
10
|
+
from DIRACCommon.Core.Utilities.ReturnValues import * # noqa: F401, F403
|
|
@@ -1,185 +1,19 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Backward compatibility wrapper - moved to DIRACCommon
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class State:
|
|
9
|
-
"""
|
|
10
|
-
State class that represents a single step on a StateMachine, with all the
|
|
11
|
-
possible transitions, the default transition and an ordering level.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
examples:
|
|
15
|
-
>>> s0 = State(100)
|
|
16
|
-
>>> s1 = State(0, ['StateName1', 'StateName2'], defState='StateName1')
|
|
17
|
-
>>> s2 = State(0, ['StateName1', 'StateName2'])
|
|
18
|
-
# this example is tricky. The transition rule says that will go to
|
|
19
|
-
# nextState, e.g. 'StateNext'. But, it is not on the stateMap, and there
|
|
20
|
-
# is no default defined, so it will end up going to StateNext anyway. You
|
|
21
|
-
# must be careful while defining states and their stateMaps and defaults.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
def __init__(self, level, stateMap=None, defState=None):
|
|
25
|
-
"""
|
|
26
|
-
:param int level: each state is mapped to an integer, which is used to sort the states according to that integer.
|
|
27
|
-
:param list stateMap: it is a list (of strings) with the reachable states from this particular status.
|
|
28
|
-
If not defined, we assume there are no restrictions.
|
|
29
|
-
:param str defState: default state used in case the next state is not in stateMap (not defined or simply not there).
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
self.level = level
|
|
33
|
-
self.stateMap = stateMap if stateMap else []
|
|
34
|
-
self.default = defState
|
|
35
|
-
|
|
36
|
-
def transitionRule(self, nextState):
|
|
37
|
-
"""
|
|
38
|
-
Method that selects next state, knowing the default and the transitions
|
|
39
|
-
map, and the proposed next state. If <nextState> is in stateMap, goes there.
|
|
40
|
-
If not, then goes to <self.default> if any. Otherwise, goes to <nextState>
|
|
41
|
-
anyway.
|
|
42
|
-
|
|
43
|
-
examples:
|
|
44
|
-
>>> s0.transitionRule('nextState')
|
|
45
|
-
'nextState'
|
|
46
|
-
>>> s1.transitionRule('StateName2')
|
|
47
|
-
'StateName2'
|
|
48
|
-
>>> s1.transitionRule('StateNameNotInMap')
|
|
49
|
-
'StateName1'
|
|
50
|
-
>>> s2.transitionRule('StateNameNotInMap')
|
|
51
|
-
'StateNameNotInMap'
|
|
52
|
-
|
|
53
|
-
:param str nextState: name of the state in the stateMap
|
|
54
|
-
:return: state name
|
|
55
|
-
:rtype: str
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
# If next state is on the list of next states, go ahead.
|
|
59
|
-
if nextState in self.stateMap:
|
|
60
|
-
return nextState
|
|
61
|
-
|
|
62
|
-
# If not, calculate defaultState:
|
|
63
|
-
# if there is a default, that one
|
|
64
|
-
# otherwise is nextState (states with empty list have no movement restrictions)
|
|
65
|
-
defaultNext = self.default if self.default else nextState
|
|
66
|
-
return defaultNext
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class StateMachine:
|
|
70
|
-
"""
|
|
71
|
-
StateMachine class that represents the whole state machine with all transitions.
|
|
72
|
-
|
|
73
|
-
examples:
|
|
74
|
-
>>> sm0 = StateMachine()
|
|
75
|
-
>>> sm1 = StateMachine(state = 'Active')
|
|
76
|
-
|
|
77
|
-
:param state: current state of the StateMachine, could be None if we do not use the
|
|
78
|
-
StateMachine to calculate transitions. Beware, it is not checked if the
|
|
79
|
-
state is on the states map !
|
|
80
|
-
:type state: None or str
|
|
81
|
-
|
|
82
|
-
"""
|
|
3
|
+
This module has been moved to DIRACCommon.Core.Utilities.StateMachine to avoid
|
|
4
|
+
circular dependencies and allow DiracX to use these utilities without
|
|
5
|
+
triggering DIRAC's global state initialization.
|
|
83
6
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self.state = state
|
|
90
|
-
# To be overwritten by child classes, unless you like Nirvana state that much.
|
|
91
|
-
self.states = {"Nirvana": State(100)}
|
|
92
|
-
|
|
93
|
-
def getLevelOfState(self, state):
|
|
94
|
-
"""
|
|
95
|
-
Given a state name, it returns its level (integer), which defines the hierarchy.
|
|
7
|
+
All exports are maintained for backward compatibility.
|
|
8
|
+
"""
|
|
9
|
+
# Re-export everything from DIRACCommon for backward compatibility
|
|
10
|
+
from DIRACCommon.Core.Utilities.StateMachine import * # noqa: F401, F403
|
|
96
11
|
|
|
97
|
-
|
|
98
|
-
100
|
|
99
|
-
>>> sm0.getLevelOfState('AnotherState')
|
|
100
|
-
-1
|
|
12
|
+
from DIRAC import gLogger
|
|
101
13
|
|
|
102
|
-
:param str state: name of the state, it should be on <self.states> key set
|
|
103
|
-
:return: `int` || -1 (if not in <self.states>)
|
|
104
|
-
"""
|
|
105
14
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return self.states[state].level
|
|
15
|
+
class StateMachine(StateMachine): # noqa: F405 pylint: disable=function-redefined
|
|
16
|
+
"""Backward compatibility wrapper - moved to DIRACCommon"""
|
|
109
17
|
|
|
110
18
|
def setState(self, candidateState, noWarn=False):
|
|
111
|
-
|
|
112
|
-
Final states are also checked.
|
|
113
|
-
|
|
114
|
-
examples:
|
|
115
|
-
>>> sm0.setState(None)['OK']
|
|
116
|
-
True
|
|
117
|
-
>>> sm0.setState('Nirvana')['OK']
|
|
118
|
-
True
|
|
119
|
-
>>> sm0.setState('AnotherState')['OK']
|
|
120
|
-
False
|
|
121
|
-
|
|
122
|
-
:param state: state which will be set as current state of the StateMachine
|
|
123
|
-
:type state: None or str
|
|
124
|
-
:return: S_OK || S_ERROR
|
|
125
|
-
"""
|
|
126
|
-
if candidateState == self.state:
|
|
127
|
-
return S_OK(candidateState)
|
|
128
|
-
|
|
129
|
-
if not candidateState:
|
|
130
|
-
self.state = candidateState
|
|
131
|
-
elif candidateState in self.states:
|
|
132
|
-
if not self.states[self.state].stateMap:
|
|
133
|
-
if not noWarn:
|
|
134
|
-
gLogger.warn("Final state, won't move", f"({self.state}, asked to move to {candidateState})")
|
|
135
|
-
return S_OK(self.state)
|
|
136
|
-
if candidateState not in self.states[self.state].stateMap:
|
|
137
|
-
gLogger.warn(f"Can't move from {self.state} to {candidateState}, choosing a good one")
|
|
138
|
-
result = self.getNextState(candidateState)
|
|
139
|
-
if not result["OK"]:
|
|
140
|
-
return result
|
|
141
|
-
self.state = result["Value"]
|
|
142
|
-
# If the StateMachine does not accept the candidate, return error message
|
|
143
|
-
else:
|
|
144
|
-
return S_ERROR(f"setState: {candidateState!r} is not a valid state")
|
|
145
|
-
|
|
146
|
-
return S_OK(self.state)
|
|
147
|
-
|
|
148
|
-
def getStates(self):
|
|
149
|
-
"""
|
|
150
|
-
Returns all possible states in the state map
|
|
151
|
-
|
|
152
|
-
examples:
|
|
153
|
-
>>> sm0.getStates()
|
|
154
|
-
[ 'Nirvana' ]
|
|
155
|
-
|
|
156
|
-
:return: list(stateNames)
|
|
157
|
-
"""
|
|
158
|
-
|
|
159
|
-
return list(self.states)
|
|
160
|
-
|
|
161
|
-
def getNextState(self, candidateState):
|
|
162
|
-
"""
|
|
163
|
-
Method that gets the next state, given the proposed transition to candidateState.
|
|
164
|
-
If candidateState is not on the state map <self.states>, it is rejected. If it is
|
|
165
|
-
not the case, we have two options: if <self.state> is None, then the next state
|
|
166
|
-
will be <candidateState>. Otherwise, the current state is using its own
|
|
167
|
-
transition rule to decide.
|
|
168
|
-
|
|
169
|
-
examples:
|
|
170
|
-
>>> sm0.getNextState(None)
|
|
171
|
-
S_OK(None)
|
|
172
|
-
>>> sm0.getNextState('NextState')
|
|
173
|
-
S_OK('NextState')
|
|
174
|
-
|
|
175
|
-
:param str candidateState: name of the next state
|
|
176
|
-
:return: S_OK(nextState) || S_ERROR
|
|
177
|
-
"""
|
|
178
|
-
if candidateState not in self.states:
|
|
179
|
-
return S_ERROR(f"getNextState: {candidateState!r} is not a valid state")
|
|
180
|
-
|
|
181
|
-
# FIXME: do we need this anymore ?
|
|
182
|
-
if self.state is None:
|
|
183
|
-
return S_OK(candidateState)
|
|
184
|
-
|
|
185
|
-
return S_OK(self.states[self.state].transitionRule(candidateState))
|
|
19
|
+
return super().setState(candidateState, noWarn, logger_warn=gLogger.warn)
|