DIRACCommon 9.0.0__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.
- DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py +52 -0
- DIRACCommon/ConfigurationSystem/Client/Helpers/__init__.py +3 -0
- DIRACCommon/ConfigurationSystem/Client/__init__.py +3 -0
- DIRACCommon/ConfigurationSystem/__init__.py +3 -0
- DIRACCommon/Core/Utilities/ClassAd/ClassAdLight.py +295 -0
- DIRACCommon/Core/Utilities/ClassAd/__init__.py +1 -0
- DIRACCommon/Core/Utilities/DErrno.py +327 -0
- DIRACCommon/Core/Utilities/JDL.py +199 -0
- DIRACCommon/Core/Utilities/List.py +127 -0
- DIRACCommon/Core/Utilities/ReturnValues.py +255 -0
- DIRACCommon/Core/Utilities/StateMachine.py +185 -0
- DIRACCommon/Core/Utilities/TimeUtilities.py +259 -0
- DIRACCommon/Core/Utilities/__init__.py +3 -0
- DIRACCommon/Core/__init__.py +1 -0
- DIRACCommon/WorkloadManagementSystem/Client/JobState/JobManifest.py +235 -0
- DIRACCommon/WorkloadManagementSystem/Client/JobState/__init__.py +0 -0
- DIRACCommon/WorkloadManagementSystem/Client/JobStatus.py +95 -0
- DIRACCommon/WorkloadManagementSystem/Client/__init__.py +1 -0
- DIRACCommon/WorkloadManagementSystem/DB/JobDBUtils.py +170 -0
- DIRACCommon/WorkloadManagementSystem/DB/__init__.py +1 -0
- DIRACCommon/WorkloadManagementSystem/Utilities/JobModel.py +236 -0
- DIRACCommon/WorkloadManagementSystem/Utilities/JobStatusUtility.py +93 -0
- DIRACCommon/WorkloadManagementSystem/Utilities/ParametricJob.py +179 -0
- DIRACCommon/WorkloadManagementSystem/Utilities/__init__.py +1 -0
- DIRACCommon/WorkloadManagementSystem/__init__.py +1 -0
- DIRACCommon/__init__.py +21 -0
- diraccommon-9.0.0.dist-info/METADATA +281 -0
- diraccommon-9.0.0.dist-info/RECORD +29 -0
- diraccommon-9.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
""" Utilities to process parametric job definitions and generate
|
|
2
|
+
bunches of parametric jobs. It exposes the following functions:
|
|
3
|
+
|
|
4
|
+
getParameterVectorLength() - to get the total size of the bunch of parametric jobs
|
|
5
|
+
generateParametricJobs() - to get a list of expanded descriptions of all the jobs
|
|
6
|
+
"""
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
from DIRACCommon.Core.Utilities.ClassAd.ClassAdLight import ClassAd
|
|
10
|
+
from DIRACCommon.Core.Utilities.ReturnValues import S_OK, S_ERROR
|
|
11
|
+
from DIRACCommon.Core.Utilities.DErrno import EWMSJDL
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def __getParameterSequence(nPar, parList=[], parStart=1, parStep=0, parFactor=1):
|
|
15
|
+
if parList:
|
|
16
|
+
if nPar != len(parList):
|
|
17
|
+
return []
|
|
18
|
+
else:
|
|
19
|
+
parameterList = list(parList)
|
|
20
|
+
else:
|
|
21
|
+
# The first parameter must have the same type as the other ones even if not defined explicitly
|
|
22
|
+
parameterList = [parStart * type(parFactor)(1) + type(parStep)(0)]
|
|
23
|
+
for np in range(1, nPar):
|
|
24
|
+
parameterList.append(parameterList[np - 1] * parFactor + parStep)
|
|
25
|
+
|
|
26
|
+
return parameterList
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def getParameterVectorLength(jobClassAd):
|
|
30
|
+
"""Get the length of parameter vector in the parametric job description
|
|
31
|
+
|
|
32
|
+
:param jobClassAd: ClassAd job description object
|
|
33
|
+
:return: result structure with the Value: int number of parameter values, None if not a parametric job
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
nParValues = None
|
|
37
|
+
attributes = jobClassAd.getAttributes()
|
|
38
|
+
for attribute in attributes:
|
|
39
|
+
if attribute.startswith("Parameters"):
|
|
40
|
+
if jobClassAd.isAttributeList(attribute):
|
|
41
|
+
parameterList = jobClassAd.getListFromExpression(attribute)
|
|
42
|
+
nThisParValues = len(parameterList)
|
|
43
|
+
else:
|
|
44
|
+
nThisParValues = jobClassAd.getAttributeInt(attribute)
|
|
45
|
+
if nParValues is not None and nParValues != nThisParValues:
|
|
46
|
+
return S_ERROR(
|
|
47
|
+
EWMSJDL,
|
|
48
|
+
"Different length of parameter vectors: for %s, %s != %d" % (attribute, nParValues, nThisParValues),
|
|
49
|
+
)
|
|
50
|
+
nParValues = nThisParValues
|
|
51
|
+
if nParValues is not None and nParValues <= 0:
|
|
52
|
+
return S_ERROR(EWMSJDL, "Illegal number of job parameters %d" % (nParValues))
|
|
53
|
+
return S_OK(nParValues)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def __updateAttribute(classAd, attribute, parName, parValue):
|
|
57
|
+
# If there is something to do:
|
|
58
|
+
pattern = r"%%\(%s\)s" % parName
|
|
59
|
+
if parName == "0":
|
|
60
|
+
pattern = "%s"
|
|
61
|
+
expr = classAd.get_expression(attribute)
|
|
62
|
+
if not re.search(pattern, expr):
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
pattern = "%%(%s)s" % parName
|
|
66
|
+
if parName == "0":
|
|
67
|
+
pattern = "%s"
|
|
68
|
+
|
|
69
|
+
parValue = parValue.strip()
|
|
70
|
+
if classAd.isAttributeList(attribute):
|
|
71
|
+
parValue = parValue.strip()
|
|
72
|
+
if parValue.startswith("{"):
|
|
73
|
+
parValue = parValue.lstrip("{").rstrip("}").strip()
|
|
74
|
+
|
|
75
|
+
expr = classAd.get_expression(attribute)
|
|
76
|
+
newexpr = expr.replace(pattern, str(parValue))
|
|
77
|
+
classAd.set_expression(attribute, newexpr)
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def generateParametricJobs(jobClassAd):
|
|
82
|
+
"""Generate a series of ClassAd job descriptions expanding
|
|
83
|
+
job parameters
|
|
84
|
+
|
|
85
|
+
:param jobClassAd: ClassAd job description object
|
|
86
|
+
:return: list of ClassAd job description objects
|
|
87
|
+
"""
|
|
88
|
+
if not jobClassAd.lookupAttribute("Parameters"):
|
|
89
|
+
return S_OK([jobClassAd.asJDL()])
|
|
90
|
+
|
|
91
|
+
result = getParameterVectorLength(jobClassAd)
|
|
92
|
+
if not result["OK"]:
|
|
93
|
+
return result
|
|
94
|
+
nParValues = result["Value"]
|
|
95
|
+
if nParValues is None:
|
|
96
|
+
return S_ERROR(EWMSJDL, "Can not determine the number of job parameters")
|
|
97
|
+
|
|
98
|
+
parameterDict = {}
|
|
99
|
+
attributes = jobClassAd.getAttributes()
|
|
100
|
+
for attribute in attributes:
|
|
101
|
+
for key in ["Parameters", "ParameterStart", "ParameterStep", "ParameterFactor"]:
|
|
102
|
+
if attribute.startswith(key):
|
|
103
|
+
seqID = "0" if "." not in attribute else attribute.split(".")[1]
|
|
104
|
+
parameterDict.setdefault(seqID, {})
|
|
105
|
+
if key == "Parameters":
|
|
106
|
+
if jobClassAd.isAttributeList(attribute):
|
|
107
|
+
parList = jobClassAd.getListFromExpression(attribute)
|
|
108
|
+
if len(parList) != nParValues:
|
|
109
|
+
return S_ERROR(EWMSJDL, "Inconsistent parametric job description")
|
|
110
|
+
parameterDict[seqID]["ParameterList"] = parList
|
|
111
|
+
else:
|
|
112
|
+
if attribute != "Parameters":
|
|
113
|
+
return S_ERROR(EWMSJDL, "Inconsistent parametric job description")
|
|
114
|
+
nPar = jobClassAd.getAttributeInt(attribute)
|
|
115
|
+
if nPar is None:
|
|
116
|
+
value = jobClassAd.get_expression(attribute)
|
|
117
|
+
return S_ERROR(EWMSJDL, f"Inconsistent parametric job description: {attribute}={value}")
|
|
118
|
+
parameterDict[seqID]["Parameters"] = nPar
|
|
119
|
+
else:
|
|
120
|
+
value = jobClassAd.getAttributeInt(attribute)
|
|
121
|
+
if value is None:
|
|
122
|
+
value = jobClassAd.getAttributeFloat(attribute)
|
|
123
|
+
if value is None:
|
|
124
|
+
value = jobClassAd.get_expression(attribute)
|
|
125
|
+
return S_ERROR(f"Illegal value for {attribute} JDL field: {value}")
|
|
126
|
+
parameterDict[seqID][key] = value
|
|
127
|
+
|
|
128
|
+
if "0" in parameterDict and not parameterDict.get("0"):
|
|
129
|
+
parameterDict.pop("0")
|
|
130
|
+
|
|
131
|
+
parameterLists = {}
|
|
132
|
+
for seqID in parameterDict:
|
|
133
|
+
parList = __getParameterSequence(
|
|
134
|
+
nParValues,
|
|
135
|
+
parList=parameterDict[seqID].get("ParameterList", []),
|
|
136
|
+
parStart=parameterDict[seqID].get("ParameterStart", 1),
|
|
137
|
+
parStep=parameterDict[seqID].get("ParameterStep", 0),
|
|
138
|
+
parFactor=parameterDict[seqID].get("ParameterFactor", 1),
|
|
139
|
+
)
|
|
140
|
+
if not parList:
|
|
141
|
+
return S_ERROR(EWMSJDL, "Inconsistent parametric job description")
|
|
142
|
+
|
|
143
|
+
parameterLists[seqID] = parList
|
|
144
|
+
|
|
145
|
+
jobDescList = []
|
|
146
|
+
jobDesc = jobClassAd.asJDL()
|
|
147
|
+
# Width of the sequential parameter number
|
|
148
|
+
zLength = len(str(nParValues - 1))
|
|
149
|
+
for n in range(nParValues):
|
|
150
|
+
newJobDesc = jobDesc
|
|
151
|
+
newJobDesc = newJobDesc.replace("%n", str(n).zfill(zLength))
|
|
152
|
+
newClassAd = ClassAd(newJobDesc)
|
|
153
|
+
for seqID in parameterLists:
|
|
154
|
+
parameter = parameterLists[seqID][n]
|
|
155
|
+
for attribute in newClassAd.getAttributes():
|
|
156
|
+
__updateAttribute(newClassAd, attribute, seqID, str(parameter))
|
|
157
|
+
|
|
158
|
+
for seqID in parameterLists:
|
|
159
|
+
for attribute in ["Parameters", "ParameterStart", "ParameterStep", "ParameterFactor"]:
|
|
160
|
+
if seqID == "0":
|
|
161
|
+
newClassAd.deleteAttribute(attribute)
|
|
162
|
+
else:
|
|
163
|
+
newClassAd.deleteAttribute(f"{attribute}.{seqID}")
|
|
164
|
+
|
|
165
|
+
parameter = parameterLists[seqID][n]
|
|
166
|
+
if seqID == "0":
|
|
167
|
+
attribute = "Parameter"
|
|
168
|
+
else:
|
|
169
|
+
attribute = f"Parameter.{seqID}"
|
|
170
|
+
if isinstance(parameter, str) and parameter.startswith("{"):
|
|
171
|
+
newClassAd.insertAttributeInt(attribute, str(parameter))
|
|
172
|
+
else:
|
|
173
|
+
newClassAd.insertAttributeString(attribute, str(parameter))
|
|
174
|
+
|
|
175
|
+
newClassAd.insertAttributeInt("ParameterNumber", n)
|
|
176
|
+
newJDL = newClassAd.asJDL()
|
|
177
|
+
jobDescList.append(newJDL)
|
|
178
|
+
|
|
179
|
+
return S_OK(jobDescList)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""DIRACCommon WorkloadManagementSystem utilities"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""DIRACCommon WorkloadManagementSystem utilities"""
|
DIRACCommon/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DIRACCommon - Stateless utilities for DIRAC
|
|
3
|
+
|
|
4
|
+
This package contains stateless utilities extracted from DIRAC that can be used
|
|
5
|
+
by DiracX and other projects without triggering DIRAC's global state initialization.
|
|
6
|
+
|
|
7
|
+
The utilities here should not depend on:
|
|
8
|
+
- gConfig (Configuration system)
|
|
9
|
+
- gLogger (Global logging)
|
|
10
|
+
- gMonitor (Monitoring)
|
|
11
|
+
- Database connections
|
|
12
|
+
- Any other global state
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import importlib.metadata
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
__version__ = importlib.metadata.version(__name__)
|
|
19
|
+
except importlib.metadata.PackageNotFoundError:
|
|
20
|
+
# package is not installed
|
|
21
|
+
__version__ = "Unknown"
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: DIRACCommon
|
|
3
|
+
Version: 9.0.0
|
|
4
|
+
Summary: Stateless utilities extracted from DIRAC for use by DiracX and other projects
|
|
5
|
+
Project-URL: Homepage, https://github.com/DIRACGrid/DIRAC
|
|
6
|
+
Project-URL: Documentation, https://dirac.readthedocs.io/
|
|
7
|
+
Project-URL: Source Code, https://github.com/DIRACGrid/DIRAC
|
|
8
|
+
Author-email: DIRAC Collaboration <dirac-dev@cern.ch>
|
|
9
|
+
License: GPL-3.0-only
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: diraccfg
|
|
18
|
+
Requires-Dist: pydantic>=2.0.0
|
|
19
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
20
|
+
Provides-Extra: testing
|
|
21
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'testing'
|
|
22
|
+
Requires-Dist: pytest>=7.0.0; extra == 'testing'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# DIRACCommon
|
|
26
|
+
|
|
27
|
+
Stateless utilities extracted from DIRAC for use by DiracX and other projects without triggering DIRAC's global state initialization.
|
|
28
|
+
|
|
29
|
+
## Purpose
|
|
30
|
+
|
|
31
|
+
This package solves the circular dependency issue where DiracX needs DIRAC utilities but importing DIRAC triggers global state initialization. DIRACCommon contains only stateless utilities that can be safely imported without side effects.
|
|
32
|
+
|
|
33
|
+
## Contents
|
|
34
|
+
|
|
35
|
+
- `DIRACCommon.Core.Utilities.ReturnValues`: DIRAC's S_OK/S_ERROR return value system
|
|
36
|
+
- `DIRACCommon.Core.Utilities.DErrno`: DIRAC error codes and utilities
|
|
37
|
+
- `DIRACCommon.Core.Utilities.ClassAd.ClassAdLight`: JDL parsing utilities
|
|
38
|
+
- `DIRACCommon.Core.Utilities.TimeUtilities`: Time and date utilities
|
|
39
|
+
- `DIRACCommon.Core.Utilities.StateMachine`: State machine utilities
|
|
40
|
+
- `DIRACCommon.Core.Utilities.JDL`: JDL parsing utilities
|
|
41
|
+
- `DIRACCommon.Core.Utilities.List`: List manipulation utilities
|
|
42
|
+
- `DIRACCommon.ConfigurationSystem.Client.Helpers.Resources`: Platform compatibility utilities
|
|
43
|
+
- `DIRACCommon.WorkloadManagementSystem.Client.JobStatus`: Job status constants and state machines
|
|
44
|
+
- `DIRACCommon.WorkloadManagementSystem.Client.JobState.JobManifest`: Job manifest utilities
|
|
45
|
+
- `DIRACCommon.WorkloadManagementSystem.DB.JobDBUtils`: Job database utilities
|
|
46
|
+
- `DIRACCommon.WorkloadManagementSystem.Utilities.JobModel`: Pydantic-based job models
|
|
47
|
+
- `DIRACCommon.WorkloadManagementSystem.Utilities.JobStatusUtility`: Job status utilities
|
|
48
|
+
- `DIRACCommon.WorkloadManagementSystem.Utilities.ParametricJob`: Parametric job utilities
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install DIRACCommon
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Basic Usage
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from DIRACCommon.Core.Utilities.ReturnValues import S_OK, S_ERROR
|
|
62
|
+
|
|
63
|
+
def my_function():
|
|
64
|
+
if success:
|
|
65
|
+
return S_OK("Operation successful")
|
|
66
|
+
else:
|
|
67
|
+
return S_ERROR("Operation failed")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Development
|
|
71
|
+
|
|
72
|
+
This package is part of the DIRAC project and shares its version number. When DIRAC is released, DIRACCommon is also released with the same version.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pixi install
|
|
76
|
+
pixi run pytest
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Migrating Code to DIRACCommon
|
|
80
|
+
|
|
81
|
+
This section documents the proper pattern for moving code from DIRAC to DIRACCommon to enable shared usage by DiracX and other projects.
|
|
82
|
+
|
|
83
|
+
### Migration Pattern
|
|
84
|
+
|
|
85
|
+
The migration follows a specific pattern to maintain backward compatibility while making code stateless:
|
|
86
|
+
|
|
87
|
+
1. **Move core functionality to DIRACCommon** - Create the stateless version
|
|
88
|
+
2. **Update DIRAC module** - Make it a backward compatibility wrapper
|
|
89
|
+
3. **Move and update tests** - Ensure both versions are tested
|
|
90
|
+
4. **Verify migration** - Test both import paths work correctly
|
|
91
|
+
|
|
92
|
+
### Step-by-Step Migration Process
|
|
93
|
+
|
|
94
|
+
#### 1. Create DIRACCommon Module
|
|
95
|
+
|
|
96
|
+
Create the new module in DIRACCommon with the **exact same directory structure** as DIRAC:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Example: Moving from src/DIRAC/ConfigurationSystem/Client/Helpers/Resources.py
|
|
100
|
+
# Create: dirac-common/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### 2. Make Code Stateless
|
|
104
|
+
|
|
105
|
+
**❌ Remove these dependencies:**
|
|
106
|
+
```python
|
|
107
|
+
# DON'T import these in DIRACCommon
|
|
108
|
+
from DIRAC import gConfig, gLogger, gMonitor, Operations
|
|
109
|
+
from DIRAC.Core.Security import getProxyInfo
|
|
110
|
+
# Any other DIRAC global state
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**✅ Use these instead:**
|
|
114
|
+
```python
|
|
115
|
+
# Use DIRACCommon's own utilities
|
|
116
|
+
from DIRACCommon.Core.Utilities.ReturnValues import S_OK, S_ERROR
|
|
117
|
+
from DIRACCommon.Core.Utilities.DErrno import strerror
|
|
118
|
+
|
|
119
|
+
# Accept configuration data as parameters
|
|
120
|
+
def my_function(data, config_dict):
|
|
121
|
+
# Use config_dict instead of gConfig.getOptionsDict()
|
|
122
|
+
pass
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### 3. Handle Configuration Data
|
|
126
|
+
|
|
127
|
+
**❌ Don't do this:**
|
|
128
|
+
```python
|
|
129
|
+
# DIRACCommon function taking config object
|
|
130
|
+
def getDIRACPlatform(OSList, config):
|
|
131
|
+
result = config.getOptionsDict("/Resources/Computing/OSCompatibility")
|
|
132
|
+
# ...
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**✅ Do this instead:**
|
|
136
|
+
```python
|
|
137
|
+
# DIRACCommon function taking configuration data directly
|
|
138
|
+
def getDIRACPlatform(osList: str | list[str], osCompatibilityDict: dict[str, set[str]]) -> DReturnType[list[str]]:
|
|
139
|
+
if not osCompatibilityDict:
|
|
140
|
+
return S_ERROR("OS compatibility info not found")
|
|
141
|
+
# Use osCompatibilityDict directly
|
|
142
|
+
# ...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### 4. Update DIRAC Module for Backward Compatibility
|
|
146
|
+
|
|
147
|
+
Transform the original DIRAC module into a backward compatibility wrapper:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
"""Backward compatibility wrapper - moved to DIRACCommon
|
|
151
|
+
|
|
152
|
+
This module has been moved to DIRACCommon.ConfigurationSystem.Client.Helpers.Resources
|
|
153
|
+
to avoid circular dependencies and allow DiracX to use these utilities without
|
|
154
|
+
triggering DIRAC's global state initialization.
|
|
155
|
+
|
|
156
|
+
All exports are maintained for backward compatibility.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
# Re-export everything from DIRACCommon for backward compatibility
|
|
160
|
+
from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import (
|
|
161
|
+
getDIRACPlatform as _getDIRACPlatform,
|
|
162
|
+
_platformSortKey,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
from DIRAC import S_ERROR, S_OK, gConfig
|
|
166
|
+
|
|
167
|
+
def getDIRACPlatform(OSList):
|
|
168
|
+
"""Get standard DIRAC platform(s) compatible with the argument.
|
|
169
|
+
|
|
170
|
+
Backward compatibility wrapper that uses gConfig.
|
|
171
|
+
"""
|
|
172
|
+
result = gConfig.getOptionsDict("/Resources/Computing/OSCompatibility")
|
|
173
|
+
if not (result["OK"] and result["Value"]):
|
|
174
|
+
return S_ERROR("OS compatibility info not found")
|
|
175
|
+
|
|
176
|
+
# Convert string values to sets for DIRACCommon function
|
|
177
|
+
platformsDict = {k: set(v.replace(" ", "").split(",")) for k, v in result["Value"].items()}
|
|
178
|
+
for k, v in platformsDict.items():
|
|
179
|
+
if k not in v:
|
|
180
|
+
v.add(k)
|
|
181
|
+
|
|
182
|
+
return _getDIRACPlatform(OSList, platformsDict)
|
|
183
|
+
|
|
184
|
+
# Re-export the helper function
|
|
185
|
+
_platformSortKey = _platformSortKey
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### 5. Move and Update Tests
|
|
189
|
+
|
|
190
|
+
**Create DIRACCommon tests:**
|
|
191
|
+
```python
|
|
192
|
+
# dirac-common/tests/ConfigurationSystem/Client/Helpers/test_Resources.py
|
|
193
|
+
from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
|
|
194
|
+
|
|
195
|
+
def test_getDIRACPlatform():
|
|
196
|
+
# Test with configuration data directly
|
|
197
|
+
osCompatibilityDict = {
|
|
198
|
+
"plat1": {"OS1", "OS2"},
|
|
199
|
+
"plat2": {"OS3", "OS4"}
|
|
200
|
+
}
|
|
201
|
+
result = getDIRACPlatform("OS1", osCompatibilityDict)
|
|
202
|
+
assert result["OK"]
|
|
203
|
+
assert "plat1" in result["Value"]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Update DIRAC tests:**
|
|
207
|
+
```python
|
|
208
|
+
# src/DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py
|
|
209
|
+
from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
|
|
210
|
+
|
|
211
|
+
def test_getDIRACPlatform():
|
|
212
|
+
# Test backward compatibility wrapper
|
|
213
|
+
# (existing tests should continue to work)
|
|
214
|
+
pass
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### 6. Create Directory Structure
|
|
218
|
+
|
|
219
|
+
Ensure the DIRACCommon directory structure mirrors DIRAC exactly:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
dirac-common/src/DIRACCommon/
|
|
223
|
+
├── ConfigurationSystem/
|
|
224
|
+
│ ├── __init__.py
|
|
225
|
+
│ └── Client/
|
|
226
|
+
│ ├── __init__.py
|
|
227
|
+
│ └── Helpers/
|
|
228
|
+
│ ├── __init__.py
|
|
229
|
+
│ └── Resources.py
|
|
230
|
+
└── tests/
|
|
231
|
+
└── ConfigurationSystem/
|
|
232
|
+
├── __init__.py
|
|
233
|
+
└── Client/
|
|
234
|
+
├── __init__.py
|
|
235
|
+
└── Helpers/
|
|
236
|
+
├── __init__.py
|
|
237
|
+
└── test_Resources.py
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Requirements for DIRACCommon Code
|
|
241
|
+
|
|
242
|
+
Code in DIRACCommon **MUST** be:
|
|
243
|
+
|
|
244
|
+
- **Completely stateless** - No global variables or state
|
|
245
|
+
- **No DIRAC dependencies** - Cannot import from DIRAC
|
|
246
|
+
- **No global state access** - Cannot use `gConfig`, `gLogger`, `gMonitor`, etc.
|
|
247
|
+
- **No database connections** - Cannot establish DB connections
|
|
248
|
+
- **No side effects on import** - Importing should not trigger any initialization
|
|
249
|
+
- **Pure functions** - Functions should be deterministic and side-effect free
|
|
250
|
+
|
|
251
|
+
### Configuration Data Handling
|
|
252
|
+
|
|
253
|
+
When DIRACCommon functions need configuration data:
|
|
254
|
+
|
|
255
|
+
1. **Accept data as parameters** - Don't accept config objects
|
|
256
|
+
2. **Use specific data types** - Pass dictionaries, not config objects
|
|
257
|
+
3. **Let DIRAC wrapper handle gConfig** - DIRAC gets data and passes it to DIRACCommon
|
|
258
|
+
|
|
259
|
+
### Example Migration
|
|
260
|
+
|
|
261
|
+
See the migration of `getDIRACPlatform` in:
|
|
262
|
+
- `dirac-common/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py`
|
|
263
|
+
- `src/DIRAC/ConfigurationSystem/Client/Helpers/Resources.py`
|
|
264
|
+
|
|
265
|
+
This demonstrates the complete pattern from stateless DIRACCommon implementation to backward-compatible DIRAC wrapper.
|
|
266
|
+
|
|
267
|
+
### Testing the Migration
|
|
268
|
+
|
|
269
|
+
After migration, verify:
|
|
270
|
+
|
|
271
|
+
1. **DIRACCommon tests pass** - `pixi run python -m pytest dirac-common/tests/`
|
|
272
|
+
2. **DIRAC tests pass** - `pixi run python -m pytest src/DIRAC/`
|
|
273
|
+
3. **Both import paths work**:
|
|
274
|
+
```python
|
|
275
|
+
# DIRACCommon (stateless)
|
|
276
|
+
from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
|
|
277
|
+
|
|
278
|
+
# DIRAC (backward compatibility)
|
|
279
|
+
from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
|
|
280
|
+
```
|
|
281
|
+
4. **No linting errors** - All code should pass linting checks
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
DIRACCommon/__init__.py,sha256=o31FFvmIiU2Wsc9nfsPZVaXYK9d3KTN0vOa8mZS6x5E,587
|
|
2
|
+
DIRACCommon/ConfigurationSystem/__init__.py,sha256=zD7hqGhEaKikUOFQ9fYd-T1nGE3A7hBQnSbZMNdde_Q,74
|
|
3
|
+
DIRACCommon/ConfigurationSystem/Client/__init__.py,sha256=wc_AKSzmjk_56P_CMkihYaEX4RUiED3XZCLcwZ9NPoY,88
|
|
4
|
+
DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py,sha256=N8gRrfv3Ai-tzloNTvCb3jOMHj7PkMzKiWd-MmWu2vA,1904
|
|
5
|
+
DIRACCommon/ConfigurationSystem/Client/Helpers/__init__.py,sha256=gCIB_25s-6H77hwho9ex_nUnMX2vvzg1Rr33u1-mLkI,95
|
|
6
|
+
DIRACCommon/Core/__init__.py,sha256=Kv_Rkbm9hOPmHSnTG0_i7QR5Tck8bldITONhsoT2syg,33
|
|
7
|
+
DIRACCommon/Core/Utilities/DErrno.py,sha256=LUaIcKkjuioTMO3O7H9T9Pk5pHUS63Nzx3FcaP40n5c,7998
|
|
8
|
+
DIRACCommon/Core/Utilities/JDL.py,sha256=z4RID730tbCmbgldKfhSFc9ADfXDhUfjaBG8RpuK2Bs,6724
|
|
9
|
+
DIRACCommon/Core/Utilities/List.py,sha256=AahQrlQqfnh8tyA1GjGqYJfo_3v5N77IQreA8VCNXks,3758
|
|
10
|
+
DIRACCommon/Core/Utilities/ReturnValues.py,sha256=YDZEZrYLtI_Aa8qTnHX633F1cRupQ_UTAnI7aSlf0sI,8733
|
|
11
|
+
DIRACCommon/Core/Utilities/StateMachine.py,sha256=5Pwpc2wmCgb6PwyUH2qe9HZwBMyzkhFTBA7cixso1Sg,6767
|
|
12
|
+
DIRACCommon/Core/Utilities/TimeUtilities.py,sha256=wXuYIwECbLSFdVoNM0iSYoCrz6_iL-JrMV1yVv3FloA,9026
|
|
13
|
+
DIRACCommon/Core/Utilities/__init__.py,sha256=hwOqqYwQDINF-D3eKKBC1r-B_jEnQ5ZSdJvc7rIv_fY,56
|
|
14
|
+
DIRACCommon/Core/Utilities/ClassAd/ClassAdLight.py,sha256=7450ECzxnJQpIjzbaE-uFp3ISE55iL6YM-TRqyebZTY,9597
|
|
15
|
+
DIRACCommon/Core/Utilities/ClassAd/__init__.py,sha256=z0yRe-wRxIHE-Sp3tnyC37S6z38dZHCxuqf3ZV1OyzQ,40
|
|
16
|
+
DIRACCommon/WorkloadManagementSystem/__init__.py,sha256=XMOUD0OcPVo-mjuA0vU-mbtf9K0kBMy5I5sELrX3HEs,53
|
|
17
|
+
DIRACCommon/WorkloadManagementSystem/Client/JobStatus.py,sha256=lBfPYa_RRjJlWEU5eqrbWFaSl1081CqSDjhI6B7-qeA,2813
|
|
18
|
+
DIRACCommon/WorkloadManagementSystem/Client/__init__.py,sha256=ssMppR0P3iXlUaqWPjl1Y5AlQ6LgUAsRa9-5lVkBsL0,60
|
|
19
|
+
DIRACCommon/WorkloadManagementSystem/Client/JobState/JobManifest.py,sha256=E3DVRzAHUFAFa9baqEbAMr-4cu6tTXuVRVdl7XgUX7I,7380
|
|
20
|
+
DIRACCommon/WorkloadManagementSystem/Client/JobState/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
DIRACCommon/WorkloadManagementSystem/DB/JobDBUtils.py,sha256=YU97uk0fjwYN6O8nkD6XjnZ7k9ovhSLYjytqI3oLRfU,5777
|
|
22
|
+
DIRACCommon/WorkloadManagementSystem/DB/__init__.py,sha256=CXWy4bdKeG6ml9T5r8-E7EyfDBNE79I_bs13qYI7yY8,56
|
|
23
|
+
DIRACCommon/WorkloadManagementSystem/Utilities/JobModel.py,sha256=kpvsxQnR-j9Y9I38qUWLD7coFRssLwtJCtSNAbjFh1Y,8973
|
|
24
|
+
DIRACCommon/WorkloadManagementSystem/Utilities/JobStatusUtility.py,sha256=Kf4o1bp25JLsRb-Vsc4NuM6w1Io2hg_ALtIyqBk-Qhw,3628
|
|
25
|
+
DIRACCommon/WorkloadManagementSystem/Utilities/ParametricJob.py,sha256=iYd29TgGXZk31pQLPw_frIhcmZB9X-RmuDmIRxzgiww,7405
|
|
26
|
+
DIRACCommon/WorkloadManagementSystem/Utilities/__init__.py,sha256=XMOUD0OcPVo-mjuA0vU-mbtf9K0kBMy5I5sELrX3HEs,53
|
|
27
|
+
diraccommon-9.0.0.dist-info/METADATA,sha256=diT9PkasG5d0zhaEJFMDKtVS5XsPDBV_XtJfQVKgkS8,9735
|
|
28
|
+
diraccommon-9.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
29
|
+
diraccommon-9.0.0.dist-info/RECORD,,
|