@@ -11,7 +11,16 @@ This package solves the circular dependency issue where DiracX needs DIRAC utili
1111- ` DIRACCommon.Core.Utilities.ReturnValues ` : DIRAC's S_OK/S_ERROR return value system
1212- ` DIRACCommon.Core.Utilities.DErrno ` : DIRAC error codes and utilities
1313- ` 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
1421- ` DIRACCommon.WorkloadManagementSystem.DB.JobDBUtils ` : Job database utilities
22+ - ` DIRACCommon.WorkloadManagementSystem.Utilities.JobModel ` : Pydantic-based job models
23+ - ` DIRACCommon.WorkloadManagementSystem.Utilities.JobStatusUtility ` : Job status utilities
1524- ` DIRACCommon.WorkloadManagementSystem.Utilities.ParametricJob ` : Parametric job utilities
1625
1726## Installation
@@ -41,10 +50,196 @@ pixi install
4150pixi run pytest
4251```
4352
44- ## Guidelines for Adding Code
53+ ## Migrating Code to DIRACCommon
4554
46- Code added to DIRACCommon must:
47- - Be completely stateless
48- - Not import or use any of DIRAC's global objects (` gConfig ` , ` gLogger ` , ` gMonitor ` , ` Operations ` )
49- - Not establish database connections
50- - Not have side effects on import
55+ This section documents the proper pattern for moving code from DIRAC to DIRACCommon to enable shared usage by DiracX and other projects.
56+
57+ ### Migration Pattern
58+
59+ The migration follows a specific pattern to maintain backward compatibility while making code stateless:
60+
61+ 1 . ** Move core functionality to DIRACCommon** - Create the stateless version
62+ 2 . ** Update DIRAC module** - Make it a backward compatibility wrapper
63+ 3 . ** Move and update tests** - Ensure both versions are tested
64+ 4 . ** Verify migration** - Test both import paths work correctly
65+
66+ ### Step-by-Step Migration Process
67+
68+ #### 1. Create DIRACCommon Module
69+
70+ Create the new module in DIRACCommon with the ** exact same directory structure** as DIRAC:
71+
72+ ``` bash
73+ # Example: Moving from src/DIRAC/ConfigurationSystem/Client/Helpers/Resources.py
74+ # Create: dirac-common/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py
75+ ```
76+
77+ #### 2. Make Code Stateless
78+
79+ ** ❌ Remove these dependencies:**
80+ ``` python
81+ # DON'T import these in DIRACCommon
82+ from DIRAC import gConfig, gLogger, gMonitor, Operations
83+ from DIRAC .Core.Security import getProxyInfo
84+ # Any other DIRAC global state
85+ ```
86+
87+ ** ✅ Use these instead:**
88+ ``` python
89+ # Use DIRACCommon's own utilities
90+ from DIRACCommon.Core.Utilities.ReturnValues import S_OK , S_ERROR
91+ from DIRACCommon.Core.Utilities.DErrno import strerror
92+
93+ # Accept configuration data as parameters
94+ def my_function (data , config_dict ):
95+ # Use config_dict instead of gConfig.getOptionsDict()
96+ pass
97+ ```
98+
99+ #### 3. Handle Configuration Data
100+
101+ ** ❌ Don't do this:**
102+ ``` python
103+ # DIRACCommon function taking config object
104+ def getDIRACPlatform (OSList , config ):
105+ result = config.getOptionsDict(" /Resources/Computing/OSCompatibility" )
106+ # ...
107+ ```
108+
109+ ** ✅ Do this instead:**
110+ ``` python
111+ # DIRACCommon function taking configuration data directly
112+ def getDIRACPlatform (OSList , osCompatibilityDict ):
113+ if not osCompatibilityDict:
114+ return S_ERROR(" OS compatibility info not found" )
115+ # Use osCompatibilityDict directly
116+ # ...
117+ ```
118+
119+ #### 4. Update DIRAC Module for Backward Compatibility
120+
121+ Transform the original DIRAC module into a backward compatibility wrapper:
122+
123+ ``` python
124+ """ Backward compatibility wrapper - moved to DIRACCommon
125+
126+ This module has been moved to DIRACCommon.ConfigurationSystem.Client.Helpers.Resources
127+ to avoid circular dependencies and allow DiracX to use these utilities without
128+ triggering DIRAC's global state initialization.
129+
130+ All exports are maintained for backward compatibility.
131+ """
132+
133+ # Re-export everything from DIRACCommon for backward compatibility
134+ from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import (
135+ getDIRACPlatform as _getDIRACPlatform,
136+ _platformSortKey,
137+ )
138+
139+ from DIRAC import S_ERROR , S_OK , gConfig
140+
141+ def getDIRACPlatform (OSList ):
142+ """ Get standard DIRAC platform(s) compatible with the argument.
143+
144+ Backward compatibility wrapper that uses gConfig.
145+ """
146+ result = gConfig.getOptionsDict(" /Resources/Computing/OSCompatibility" )
147+ if not (result[" OK" ] and result[" Value" ]):
148+ return S_ERROR(" OS compatibility info not found" )
149+
150+ return _getDIRACPlatform(OSList, result[" Value" ])
151+
152+ # Re-export the helper function
153+ _platformSortKey = _platformSortKey
154+ ```
155+
156+ #### 5. Move and Update Tests
157+
158+ ** Create DIRACCommon tests:**
159+ ``` python
160+ # dirac-common/tests/ConfigurationSystem/Client/Helpers/test_Resources.py
161+ from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
162+
163+ def test_getDIRACPlatform ():
164+ # Test with configuration data directly
165+ osCompatibilityDict = {" plat1" : " OS1, OS2" , " plat2" : " OS3, OS4" }
166+ result = getDIRACPlatform(" OS1" , osCompatibilityDict)
167+ assert result[" OK" ]
168+ ```
169+
170+ ** Update DIRAC tests:**
171+ ``` python
172+ # src/DIRAC/ConfigurationSystem/Client/Helpers/test/Test_Helpers.py
173+ from DIRAC .ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
174+
175+ def test_getDIRACPlatform ():
176+ # Test backward compatibility wrapper
177+ # (existing tests should continue to work)
178+ pass
179+ ```
180+
181+ #### 6. Create Directory Structure
182+
183+ Ensure the DIRACCommon directory structure mirrors DIRAC exactly:
184+
185+ ``` bash
186+ dirac-common/src/DIRACCommon/
187+ ├── ConfigurationSystem/
188+ │ ├── __init__.py
189+ │ └── Client/
190+ │ ├── __init__.py
191+ │ └── Helpers/
192+ │ ├── __init__.py
193+ │ └── Resources.py
194+ └── tests/
195+ └── ConfigurationSystem/
196+ ├── __init__.py
197+ └── Client/
198+ ├── __init__.py
199+ └── Helpers/
200+ ├── __init__.py
201+ └── test_Resources.py
202+ ```
203+
204+ ### Requirements for DIRACCommon Code
205+
206+ Code in DIRACCommon ** MUST** be:
207+
208+ - ** Completely stateless** - No global variables or state
209+ - ** No DIRAC dependencies** - Cannot import from DIRAC
210+ - ** No global state access** - Cannot use ` gConfig ` , ` gLogger ` , ` gMonitor ` , etc.
211+ - ** No database connections** - Cannot establish DB connections
212+ - ** No side effects on import** - Importing should not trigger any initialization
213+ - ** Pure functions** - Functions should be deterministic and side-effect free
214+
215+ ### Configuration Data Handling
216+
217+ When DIRACCommon functions need configuration data:
218+
219+ 1 . ** Accept data as parameters** - Don't accept config objects
220+ 2 . ** Use specific data types** - Pass dictionaries, not config objects
221+ 3 . ** Let DIRAC wrapper handle gConfig** - DIRAC gets data and passes it to DIRACCommon
222+
223+ ### Example Migration
224+
225+ See the migration of ` getDIRACPlatform ` in:
226+ - ` dirac-common/src/DIRACCommon/ConfigurationSystem/Client/Helpers/Resources.py `
227+ - ` src/DIRAC/ConfigurationSystem/Client/Helpers/Resources.py `
228+
229+ This demonstrates the complete pattern from stateless DIRACCommon implementation to backward-compatible DIRAC wrapper.
230+
231+ ### Testing the Migration
232+
233+ After migration, verify:
234+
235+ 1 . ** DIRACCommon tests pass** - ` pixi run python -m pytest dirac-common/tests/ `
236+ 2 . ** DIRAC tests pass** - ` pixi run python -m pytest src/DIRAC/ `
237+ 3 . ** Both import paths work** :
238+ ``` python
239+ # DIRACCommon (stateless)
240+ from DIRACCommon.ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
241+
242+ # DIRAC (backward compatibility)
243+ from DIRAC .ConfigurationSystem.Client.Helpers.Resources import getDIRACPlatform
244+ ```
245+ 4 . ** No linting errors** - All code should pass linting checks
0 commit comments