DIRACCommon 9.0.0a66__tar.gz → 9.0.0a68__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.
Files changed (56) hide show
  1. diraccommon-9.0.0a68/PKG-INFO +281 -0
  2. diraccommon-9.0.0a68/README.md +257 -0
  3. {diraccommon-9.0.0a66 → diraccommon-9.0.0a68}/pyproject.toml +2 -0
  4. diraccommon-9.0.0a68/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py +52 -0
  5. diraccommon-9.0.0a68/src/DIRACCommon/ConfigurationSystem/Client/Helpers/__init__.py +3 -0
  6. diraccommon-9.0.0a68/src/DIRACCommon/ConfigurationSystem/Client/__init__.py +3 -0
  7. diraccommon-9.0.0a68/src/DIRACCommon/ConfigurationSystem/__init__.py +3 -0
  8. diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities/ClassAd/ClassAdLight.py +295 -0
  9. diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities/ClassAd/__init__.py +1 -0
  10. diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities/JDL.py +199 -0
  11. diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities/List.py +127 -0
  12. {diraccommon-9.0.0a66/src/DIRACCommon/Utils → diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities}/ReturnValues.py +1 -1
  13. diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities/StateMachine.py +185 -0
  14. diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities/TimeUtilities.py +259 -0
  15. diraccommon-9.0.0a68/src/DIRACCommon/Core/__init__.py +1 -0
  16. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Client/JobState/JobManifest.py +235 -0
  17. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Client/JobState/__init__.py +0 -0
  18. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Client/JobStatus.py +95 -0
  19. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Client/__init__.py +1 -0
  20. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/DB/JobDBUtils.py +170 -0
  21. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/DB/__init__.py +1 -0
  22. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Utilities/JobModel.py +236 -0
  23. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Utilities/JobStatusUtility.py +93 -0
  24. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Utilities/ParametricJob.py +179 -0
  25. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/Utilities/__init__.py +1 -0
  26. diraccommon-9.0.0a68/src/DIRACCommon/WorkloadManagementSystem/__init__.py +1 -0
  27. diraccommon-9.0.0a68/tests/ConfigurationSystem/Client/Helpers/__init__.py +3 -0
  28. diraccommon-9.0.0a68/tests/ConfigurationSystem/Client/Helpers/test_Resources.py +47 -0
  29. diraccommon-9.0.0a68/tests/ConfigurationSystem/Client/__init__.py +3 -0
  30. diraccommon-9.0.0a68/tests/ConfigurationSystem/__init__.py +3 -0
  31. diraccommon-9.0.0a68/tests/Core/Utilities/ClassAd/__init__.py +0 -0
  32. diraccommon-9.0.0a68/tests/Core/Utilities/ClassAd/test_ClassAdLight.py +69 -0
  33. diraccommon-9.0.0a68/tests/Core/Utilities/__init__.py +0 -0
  34. {diraccommon-9.0.0a66/tests/Utils → diraccommon-9.0.0a68/tests/Core/Utilities}/test_DErrno.py +1 -1
  35. diraccommon-9.0.0a68/tests/Core/Utilities/test_JDL.py +113 -0
  36. diraccommon-9.0.0a68/tests/Core/Utilities/test_List.py +150 -0
  37. {diraccommon-9.0.0a66/tests/Utils → diraccommon-9.0.0a68/tests/Core/Utilities}/test_ReturnValues.py +7 -1
  38. diraccommon-9.0.0a68/tests/Core/Utilities/test_TimeUtilities.py +87 -0
  39. diraccommon-9.0.0a68/tests/Core/__init__.py +0 -0
  40. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Client/JobState/__init__.py +1 -0
  41. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Client/JobState/test_JobManifest.py +129 -0
  42. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Client/__init__.py +1 -0
  43. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/DB/__init__.py +0 -0
  44. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/DB/test_JobDBUtils.py +73 -0
  45. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Utilities/__init__.py +0 -0
  46. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Utilities/test_JobModel.py +264 -0
  47. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Utilities/test_JobStatusUtility.py +164 -0
  48. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/Utilities/test_ParametricJob.py +141 -0
  49. diraccommon-9.0.0a68/tests/WorkloadManagementSystem/__init__.py +0 -0
  50. diraccommon-9.0.0a66/PKG-INFO +0 -69
  51. diraccommon-9.0.0a66/README.md +0 -47
  52. {diraccommon-9.0.0a66 → diraccommon-9.0.0a68}/.gitignore +0 -0
  53. {diraccommon-9.0.0a66/src/DIRACCommon/Utils → diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities}/DErrno.py +0 -0
  54. {diraccommon-9.0.0a66/src/DIRACCommon/Utils → diraccommon-9.0.0a68/src/DIRACCommon/Core/Utilities}/__init__.py +0 -0
  55. {diraccommon-9.0.0a66 → diraccommon-9.0.0a68}/src/DIRACCommon/__init__.py +0 -0
  56. {diraccommon-9.0.0a66 → diraccommon-9.0.0a68}/tests/__init__.py +0 -0
@@ -0,0 +1,281 @@
1
+ Metadata-Version: 2.4
2
+ Name: DIRACCommon
3
+ Version: 9.0.0a68
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,257 @@
1
+ # DIRACCommon
2
+
3
+ Stateless utilities extracted from DIRAC for use by DiracX and other projects without triggering DIRAC's global state initialization.
4
+
5
+ ## Purpose
6
+
7
+ 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.
8
+
9
+ ## Contents
10
+
11
+ - `DIRACCommon.Core.Utilities.ReturnValues`: DIRAC's S_OK/S_ERROR return value system
12
+ - `DIRACCommon.Core.Utilities.DErrno`: DIRAC error codes and utilities
13
+ - `DIRACCommon.Core.Utilities.ClassAd.ClassAdLight`: JDL parsing utilities
14
+ - `DIRACCommon.Core.Utilities.TimeUtilities`: Time and date utilities
15
+ - `DIRACCommon.Core.Utilities.StateMachine`: State machine utilities
16
+ - `DIRACCommon.Core.Utilities.JDL`: JDL parsing utilities
17
+ - `DIRACCommon.Core.Utilities.List`: List manipulation utilities
18
+ - `DIRACCommon.ConfigurationSystem.Client.Helpers.Resources`: Platform compatibility utilities
19
+ - `DIRACCommon.WorkloadManagementSystem.Client.JobStatus`: Job status constants and state machines
20
+ - `DIRACCommon.WorkloadManagementSystem.Client.JobState.JobManifest`: Job manifest utilities
21
+ - `DIRACCommon.WorkloadManagementSystem.DB.JobDBUtils`: Job database utilities
22
+ - `DIRACCommon.WorkloadManagementSystem.Utilities.JobModel`: Pydantic-based job models
23
+ - `DIRACCommon.WorkloadManagementSystem.Utilities.JobStatusUtility`: Job status utilities
24
+ - `DIRACCommon.WorkloadManagementSystem.Utilities.ParametricJob`: Parametric job utilities
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ pip install DIRACCommon
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Basic Usage
35
+
36
+ ```python
37
+ from DIRACCommon.Core.Utilities.ReturnValues import S_OK, S_ERROR
38
+
39
+ def my_function():
40
+ if success:
41
+ return S_OK("Operation successful")
42
+ else:
43
+ return S_ERROR("Operation failed")
44
+ ```
45
+
46
+ ## Development
47
+
48
+ 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.
49
+
50
+ ```bash
51
+ pixi install
52
+ pixi run pytest
53
+ ```
54
+
55
+ ## Migrating Code to DIRACCommon
56
+
57
+ This section documents the proper pattern for moving code from DIRAC to DIRACCommon to enable shared usage by DiracX and other projects.
58
+
59
+ ### Migration Pattern
60
+
61
+ The migration follows a specific pattern to maintain backward compatibility while making code stateless:
62
+
63
+ 1. **Move core functionality to DIRACCommon** - Create the stateless version
64
+ 2. **Update DIRAC module** - Make it a backward compatibility wrapper
65
+ 3. **Move and update tests** - Ensure both versions are tested
66
+ 4. **Verify migration** - Test both import paths work correctly
67
+
68
+ ### Step-by-Step Migration Process
69
+
70
+ #### 1. Create DIRACCommon Module
71
+
72
+ Create the new module in DIRACCommon with the **exact same directory structure** as DIRAC:
73
+
74
+ ```bash
75
+ # Example: Moving from src/DIRAC/ConfigurationSystem/Client/Helpers/Resources.py
76
+ # Create: dirac-common/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py
77
+ ```
78
+
79
+ #### 2. Make Code Stateless
80
+
81
+ **❌ Remove these dependencies:**
82
+ ```python
83
+ # DON'T import these in DIRACCommon
84
+ from DIRAC import gConfig, gLogger, gMonitor, Operations
85
+ from DIRAC.Core.Security import getProxyInfo
86
+ # Any other DIRAC global state
87
+ ```
88
+
89
+ **✅ Use these instead:**
90
+ ```python
91
+ # Use DIRACCommon's own utilities
92
+ from DIRACCommon.Core.Utilities.ReturnValues import S_OK, S_ERROR
93
+ from DIRACCommon.Core.Utilities.DErrno import strerror
94
+
95
+ # Accept configuration data as parameters
96
+ def my_function(data, config_dict):
97
+ # Use config_dict instead of gConfig.getOptionsDict()
98
+ pass
99
+ ```
100
+
101
+ #### 3. Handle Configuration Data
102
+
103
+ **❌ Don't do this:**
104
+ ```python
105
+ # DIRACCommon function taking config object
106
+ def getDIRACPlatform(OSList, config):
107
+ result = config.getOptionsDict("/Resources/Computing/OSCompatibility")
108
+ # ...
109
+ ```
110
+
111
+ **✅ Do this instead:**
112
+ ```python
113
+ # DIRACCommon function taking configuration data directly
114
+ def getDIRACPlatform(osList: str | list[str], osCompatibilityDict: dict[str, set[str]]) -> DReturnType[list[str]]:
115
+ if not osCompatibilityDict:
116
+ return S_ERROR("OS compatibility info not found")
117
+ # Use osCompatibilityDict directly
118
+ # ...
119
+ ```
120
+
121
+ #### 4. Update DIRAC Module for Backward Compatibility
122
+
123
+ Transform the original DIRAC module into a backward compatibility wrapper:
124
+
125
+ ```python
126
+ """Backward compatibility wrapper - moved to DIRACCommon
127
+
128
+ This module has been moved to DIRACCommon.ConfigurationSystem.Client.Helpers.Resources
129
+ to avoid circular dependencies and allow DiracX to use these utilities without
130
+ triggering DIRAC's global state initialization.
131
+
132
+ All exports are maintained for backward compatibility.
133
+ """
134
+
135
+ # Re-export everything from DIRACCommon for backward compatibility
136
+ from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import (
137
+ getDIRACPlatform as _getDIRACPlatform,
138
+ _platformSortKey,
139
+ )
140
+
141
+ from DIRAC import S_ERROR, S_OK, gConfig
142
+
143
+ def getDIRACPlatform(OSList):
144
+ """Get standard DIRAC platform(s) compatible with the argument.
145
+
146
+ Backward compatibility wrapper that uses gConfig.
147
+ """
148
+ result = gConfig.getOptionsDict("/Resources/Computing/OSCompatibility")
149
+ if not (result["OK"] and result["Value"]):
150
+ return S_ERROR("OS compatibility info not found")
151
+
152
+ # Convert string values to sets for DIRACCommon function
153
+ platformsDict = {k: set(v.replace(" ", "").split(",")) for k, v in result["Value"].items()}
154
+ for k, v in platformsDict.items():
155
+ if k not in v:
156
+ v.add(k)
157
+
158
+ return _getDIRACPlatform(OSList, platformsDict)
159
+
160
+ # Re-export the helper function
161
+ _platformSortKey = _platformSortKey
162
+ ```
163
+
164
+ #### 5. Move and Update Tests
165
+
166
+ **Create DIRACCommon tests:**
167
+ ```python
168
+ # dirac-common/tests/ConfigurationSystem/Client/Helpers/test_Resources.py
169
+ from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
170
+
171
+ def test_getDIRACPlatform():
172
+ # Test with configuration data directly
173
+ osCompatibilityDict = {
174
+ "plat1": {"OS1", "OS2"},
175
+ "plat2": {"OS3", "OS4"}
176
+ }
177
+ result = getDIRACPlatform("OS1", osCompatibilityDict)
178
+ assert result["OK"]
179
+ assert "plat1" in result["Value"]
180
+ ```
181
+
182
+ **Update DIRAC tests:**
183
+ ```python
184
+ # src/DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py
185
+ from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
186
+
187
+ def test_getDIRACPlatform():
188
+ # Test backward compatibility wrapper
189
+ # (existing tests should continue to work)
190
+ pass
191
+ ```
192
+
193
+ #### 6. Create Directory Structure
194
+
195
+ Ensure the DIRACCommon directory structure mirrors DIRAC exactly:
196
+
197
+ ```bash
198
+ dirac-common/src/DIRACCommon/
199
+ ├── ConfigurationSystem/
200
+ │ ├── __init__.py
201
+ │ └── Client/
202
+ │ ├── __init__.py
203
+ │ └── Helpers/
204
+ │ ├── __init__.py
205
+ │ └── Resources.py
206
+ └── tests/
207
+ └── ConfigurationSystem/
208
+ ├── __init__.py
209
+ └── Client/
210
+ ├── __init__.py
211
+ └── Helpers/
212
+ ├── __init__.py
213
+ └── test_Resources.py
214
+ ```
215
+
216
+ ### Requirements for DIRACCommon Code
217
+
218
+ Code in DIRACCommon **MUST** be:
219
+
220
+ - **Completely stateless** - No global variables or state
221
+ - **No DIRAC dependencies** - Cannot import from DIRAC
222
+ - **No global state access** - Cannot use `gConfig`, `gLogger`, `gMonitor`, etc.
223
+ - **No database connections** - Cannot establish DB connections
224
+ - **No side effects on import** - Importing should not trigger any initialization
225
+ - **Pure functions** - Functions should be deterministic and side-effect free
226
+
227
+ ### Configuration Data Handling
228
+
229
+ When DIRACCommon functions need configuration data:
230
+
231
+ 1. **Accept data as parameters** - Don't accept config objects
232
+ 2. **Use specific data types** - Pass dictionaries, not config objects
233
+ 3. **Let DIRAC wrapper handle gConfig** - DIRAC gets data and passes it to DIRACCommon
234
+
235
+ ### Example Migration
236
+
237
+ See the migration of `getDIRACPlatform` in:
238
+ - `dirac-common/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py`
239
+ - `src/DIRAC/ConfigurationSystem/Client/Helpers/Resources.py`
240
+
241
+ This demonstrates the complete pattern from stateless DIRACCommon implementation to backward-compatible DIRAC wrapper.
242
+
243
+ ### Testing the Migration
244
+
245
+ After migration, verify:
246
+
247
+ 1. **DIRACCommon tests pass** - `pixi run python -m pytest dirac-common/tests/`
248
+ 2. **DIRAC tests pass** - `pixi run python -m pytest src/DIRAC/`
249
+ 3. **Both import paths work**:
250
+ ```python
251
+ # DIRACCommon (stateless)
252
+ from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
253
+
254
+ # DIRAC (backward compatibility)
255
+ from DIRAC.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
256
+ ```
257
+ 4. **No linting errors** - All code should pass linting checks
@@ -21,6 +21,8 @@ classifiers = [
21
21
  ]
22
22
  dependencies = [
23
23
  "typing-extensions>=4.0.0",
24
+ "diraccfg",
25
+ "pydantic>=2.0.0",
24
26
  ]
25
27
  dynamic = ["version"]
26
28
 
@@ -0,0 +1,52 @@
1
+ """Platform utilities for DIRAC platform compatibility and management.
2
+
3
+ This module provides functions for working with DIRAC platforms and OS compatibility.
4
+ """
5
+ import re
6
+
7
+ from DIRACCommon.Core.Utilities.ReturnValues import S_ERROR, S_OK, DReturnType
8
+
9
+
10
+ def getDIRACPlatform(osList: list[str], osCompatibilityDict: dict[str, set[str]]) -> DReturnType[list[str]]:
11
+ """Get standard DIRAC platform(s) compatible with the argument.
12
+
13
+ NB: The returned value is a list, ordered by numeric components in the platform.
14
+ In practice the "highest" version (which should be the most "desirable" one is returned first)
15
+
16
+ :param list osList: list of platforms defined by resource providers
17
+ :param dict osCompatibilityDict: dictionary with OS compatibility information
18
+ :return: a list of DIRAC platforms that can be specified in job descriptions
19
+ """
20
+
21
+ if not osCompatibilityDict:
22
+ return S_ERROR("OS compatibility info not found")
23
+
24
+ # making an OS -> platforms dict
25
+ os2PlatformDict = dict()
26
+ for platform, osItems in osCompatibilityDict.items():
27
+ for osItem in osItems:
28
+ os2PlatformDict.setdefault(osItem, set()).add(platform)
29
+
30
+ platforms = set()
31
+ for os in osList:
32
+ platforms |= os2PlatformDict.get(os, set())
33
+
34
+ if not platforms:
35
+ return S_ERROR(f"No compatible DIRAC platform found for {','.join(osList)}")
36
+
37
+ return S_OK(sorted(platforms, key=_platformSortKey, reverse=True))
38
+
39
+
40
+ def _platformSortKey(version: str) -> list[str]:
41
+ # Loosely based on distutils.version.LooseVersion
42
+ parts = []
43
+ for part in re.split(r"(\d+|[a-z]+|\.| -)", version.lower()):
44
+ if not part or part == ".":
45
+ continue
46
+ if part[:1] in "0123456789":
47
+ part = part.zfill(8)
48
+ else:
49
+ while parts and parts[-1] == "00000000":
50
+ parts.pop()
51
+ parts.append(part)
52
+ return parts
@@ -0,0 +1,3 @@
1
+ """
2
+ DIRACCommon.ConfigurationSystem.Client.Helpers - Configuration system helper functions
3
+ """
@@ -0,0 +1,3 @@
1
+ """
2
+ DIRACCommon.ConfigurationSystem.Client - Configuration system client components
3
+ """
@@ -0,0 +1,3 @@
1
+ """
2
+ DIRACCommon.ConfigurationSystem - Configuration system components
3
+ """