@@ -172,177 +172,123 @@ def is_pypi_install():
172172 return os .path .exists (os .path .join (site_packages , 'hysds' ))
173173
174174
175- def get_package_config_dir (package_name , config_subdir , remote = False ):
176- """Get config directory from installed package or editable install.
175+ def get_package_resource_path (package_name , resource_path , remote = False , resource_type = 'auto' ):
176+ """Get path to package resource for both PyPI and editable installs.
177+
178+ This is the master function that handles all package resource path resolution.
177179
178180 Priority:
179- 1. Installed package shared-data location ( PyPI install)
180- 2. Editable install location (development)
181+ 1. PyPI install: share/package/resource_path
182+ 2. Editable install: ops/package/resource_path
181183
182184 :param package_name: Package name (e.g., 'hysds', 'grq2', 'mozart')
183- :param config_subdir: Config subdirectory (e.g., 'settings ', 'configs/supervisor ')
185+ :param resource_path: Relative path to resource (e.g., 'scripts/db_create.py ', 'configs/settings ')
184186 :param remote: If True, check remote host; if False, check local machine
185- :return: Full path to config directory
187+ :param resource_type: 'file', 'dir', or 'auto' (auto-detect based on test results)
188+ :return: Full path to resource
186189 """
187- import sys
188190 import sysconfig
189191
190192 base_map = {'hysds' : 'mozart' , 'grq2' : 'sciflo' , 'pele' : 'sciflo' , 'mozart' : 'mozart' }
191193 base = base_map .get (package_name , 'mozart' )
192194
193195 if remote :
194196 # Check remote host using fabric run()
195- logger .debug (f'[get_package_config_dir ] Called with package={ package_name } , config_subdir= { config_subdir } , remote=True' )
197+ logger .debug (f'[get_package_resource_path ] Called with package={ package_name } , resource= { resource_path } , remote=True, type= { resource_type } ' )
196198
197- pypi_path_cmd = f'python -c "import sysconfig, os; print(os.path.join(sysconfig.get_path(\' data\' ), \' share\' , \' { package_name } \' , \' { config_subdir } \' ))"'
198- logger .debug (f'[get_package_config_dir] Running on remote: { pypi_path_cmd } ' )
199+ # Get PyPI path from remote
200+ pypi_path_cmd = f'python -c "import sysconfig, os; print(os.path.join(sysconfig.get_path(\' data\' ), \' share\' , \' { package_name } \' , \' { resource_path } \' ))"'
201+ logger .debug (f'[get_package_resource_path] Running on remote: { pypi_path_cmd } ' )
199202 pypi_path_result = run (pypi_path_cmd , warn_only = True )
200- logger .debug (f'[get_package_config_dir ] Result succeeded={ pypi_path_result .succeeded } , return_code={ pypi_path_result .return_code } ' )
203+ logger .debug (f'[get_package_resource_path ] Result succeeded={ pypi_path_result .succeeded } , return_code={ pypi_path_result .return_code } ' )
201204
202205 if pypi_path_result .succeeded :
203206 pypi_path = pypi_path_result .strip ()
204- logger .debug (f'[get_package_config_dir ] PyPI path from remote: { pypi_path } ' )
207+ logger .debug (f'[get_package_resource_path ] PyPI path from remote: { pypi_path } ' )
205208
206- logger .debug (f'[get_package_config_dir] Checking if directory exists: test -d { pypi_path } ' )
207- check_result = run (f'test -d { pypi_path } ' , warn_only = True )
208- logger .debug (f'[get_package_config_dir] Directory exists check succeeded={ check_result .succeeded } ' )
209+ # Determine test command based on resource type
210+ if resource_type == 'file' :
211+ test_cmd = f'test -f { pypi_path } '
212+ elif resource_type == 'dir' :
213+ test_cmd = f'test -d { pypi_path } '
214+ else : # auto
215+ # Try file first, then directory
216+ test_cmd = f'test -f { pypi_path } || test -d { pypi_path } '
217+
218+ logger .debug (f'[get_package_resource_path] Checking if resource exists: { test_cmd } ' )
219+ check_result = run (test_cmd , warn_only = True )
220+ logger .debug (f'[get_package_resource_path] Resource exists check succeeded={ check_result .succeeded } ' )
209221
210222 if check_result .succeeded :
211- logger .debug (f'[get_package_config_dir ] Returning PyPI path: { pypi_path } ' )
223+ logger .debug (f'[get_package_resource_path ] Returning PyPI path: { pypi_path } ' )
212224 return pypi_path
213225 else :
214- logger .debug (f'[get_package_config_dir] Directory not found at PyPI location: { pypi_path } ' )
226+ logger .debug (f'[get_package_resource_path] Resource not found at PyPI location: { pypi_path } ' )
215227 else :
216- logger .debug (f'[get_package_config_dir ] Failed to get PyPI path from remote' )
228+ logger .debug (f'[get_package_resource_path ] Failed to get PyPI path from remote' )
217229
218230 # Fallback to editable install location
219- fallback_path = f'~/{ base } /ops/{ package_name } /{ config_subdir } '
220- logger .debug (f'[get_package_config_dir ] Returning fallback path: { fallback_path } ' )
231+ fallback_path = f'~/{ base } /ops/{ package_name } /{ resource_path } '
232+ logger .debug (f'[get_package_resource_path ] Returning fallback path: { fallback_path } ' )
221233 return fallback_path
222234 else :
223235 # Check local machine
224- share_dir = os .path .join (sysconfig .get_path ('data' ), 'share' , package_name , config_subdir )
225- if os .path .exists (share_dir ):
226- return share_dir
236+ pypi_path = os .path .join (sysconfig .get_path ('data' ), 'share' , package_name , resource_path )
237+
238+ # Check if resource exists based on type
239+ resource_exists = False
240+ if resource_type == 'file' :
241+ resource_exists = os .path .isfile (pypi_path )
242+ elif resource_type == 'dir' :
243+ resource_exists = os .path .isdir (pypi_path )
244+ else : # auto
245+ resource_exists = os .path .exists (pypi_path )
246+
247+ if resource_exists :
248+ return pypi_path
227249
228250 # Fallback to editable install location
229- editable_dir = os .path .join (ops_dir , 'mozart/ops' , package_name , config_subdir )
230- if os .path .exists (editable_dir ):
231- return editable_dir
251+ editable_path = os .path .join (ops_dir , 'mozart/ops' , package_name , resource_path )
252+ if os .path .exists (editable_path ):
253+ return editable_path
232254
233255 # Last resort: return the editable path even if it doesn't exist
234- # (will fail later with a clear error message)
235- return editable_dir
256+ return editable_path
257+
258+
259+ # Backward compatibility wrappers
260+ def get_package_config_dir (package_name , config_subdir , remote = False ):
261+ """Get config directory from installed package or editable install.
262+
263+ This is a wrapper around get_package_resource_path for backward compatibility.
264+ """
265+ return get_package_resource_path (package_name , config_subdir , remote = remote , resource_type = 'dir' )
236266
237267
238268def get_package_script_path (package_name , script_relative_path ):
239269 """Get full path to a package script file on remote host.
240270
241- For PyPI installs: Returns path in share/package/scripts/
242- For editable installs: Returns path in ops/package/scripts/
243-
244- This function checks the REMOTE host to determine the correct path.
245-
246- :param package_name: Package name (e.g., 'hysds', 'grq2', 'mozart')
247- :param script_relative_path: Relative path to script (e.g., 'scripts/install_base_es_template.sh')
248- :return: Full path to script on remote host
271+ This is a wrapper around get_package_resource_path for backward compatibility.
249272 """
250- base_map = {'hysds' : 'mozart' , 'grq2' : 'sciflo' , 'pele' : 'sciflo' , 'mozart' : 'mozart' }
251- base = base_map .get (package_name , 'mozart' )
252-
253- logger .debug (f'[get_package_script_path] Called with package={ package_name } , script={ script_relative_path } ' )
254-
255- # Try PyPI location first by checking on remote host
256- pypi_path_cmd = f'python -c "import sysconfig, os; print(os.path.join(sysconfig.get_path(\' data\' ), \' share\' , \' { package_name } \' , \' { script_relative_path } \' ))"'
257- logger .debug (f'[get_package_script_path] Running on remote: { pypi_path_cmd } ' )
258- pypi_path_result = run (pypi_path_cmd , warn_only = True )
259- logger .debug (f'[get_package_script_path] Result succeeded={ pypi_path_result .succeeded } , return_code={ pypi_path_result .return_code } ' )
260-
261- if pypi_path_result .succeeded :
262- pypi_path = pypi_path_result .strip ()
263- logger .debug (f'[get_package_script_path] PyPI path from remote: { pypi_path } ' )
264-
265- # Check if file exists at PyPI location on remote host
266- logger .debug (f'[get_package_script_path] Checking if file exists: test -f { pypi_path } ' )
267- check_result = run (f'test -f { pypi_path } ' , warn_only = True )
268- logger .debug (f'[get_package_script_path] File exists check succeeded={ check_result .succeeded } ' )
269-
270- if check_result .succeeded :
271- logger .debug (f'[get_package_script_path] Returning PyPI path: { pypi_path } ' )
272- return pypi_path
273- else :
274- logger .debug (f'[get_package_script_path] File not found at PyPI location: { pypi_path } ' )
275- else :
276- logger .debug (f'[get_package_script_path] Failed to get PyPI path from remote' )
277-
278- # Fallback to editable install location
279- fallback_path = os .path .join (ops_dir , f'{ base } /ops/{ package_name } /{ script_relative_path } ' )
280- logger .debug (f'[get_package_script_path] Returning fallback path: { fallback_path } ' )
281- return fallback_path
273+ return get_package_resource_path (package_name , script_relative_path , remote = True , resource_type = 'file' )
282274
283275
284276def copy_package_config_file (package_name , config_relative_path , dest ):
285277 """Copy a package config file to destination on remote host.
286278
287- For PyPI installs: Copies from share/package/config_relative_path
288- For editable installs: Copies from ops/package/config_relative_path
289-
290- This function runs entirely on the remote host.
291-
292- :param package_name: Package name (e.g., 'hysds', 'grq2', 'mozart')
293- :param config_relative_path: Relative path to config (e.g., 'configs/orchestrator/orchestrator_jobs.json')
294- :param dest: Destination path on remote host
279+ This is a wrapper around get_package_resource_path for backward compatibility.
295280 """
296- base_map = {'hysds' : 'mozart' , 'grq2' : 'sciflo' , 'pele' : 'sciflo' , 'mozart' : 'mozart' }
297- base = base_map .get (package_name , 'mozart' )
298-
299- # Try PyPI location first
300- pypi_path_cmd = f'python -c "import sysconfig, os; print(os.path.join(sysconfig.get_path(\' data\' ), \' share\' , \' { package_name } \' , \' { config_relative_path } \' ))"'
301- pypi_path_result = run (pypi_path_cmd , warn_only = True )
302-
303- if pypi_path_result .succeeded :
304- pypi_path = pypi_path_result .strip ()
305- check_result = run (f'test -f { pypi_path } ' , warn_only = True )
306-
307- if check_result .succeeded :
308- run (f'cp { pypi_path } { dest } ' )
309- return
310-
311- # Fallback to editable install location
312- fallback_path = f'~/{ base } /ops/{ package_name } /{ config_relative_path } '
313- run (f'cp { fallback_path } { dest } ' )
281+ src_path = get_package_resource_path (package_name , config_relative_path , remote = True , resource_type = 'file' )
282+ run (f'cp { src_path } { dest } ' )
314283
315284
316285def copy_package_dir (package_name , dir_relative_path , dest ):
317286 """Copy a package directory to destination on remote host.
318287
319- For PyPI installs: Copies from share/package/dir_relative_path
320- For editable installs: Copies from ops/package/dir_relative_path
321-
322- This function runs entirely on the remote host.
323-
324- :param package_name: Package name (e.g., 'hysds', 'grq2', 'mozart')
325- :param dir_relative_path: Relative path to directory (e.g., 'scripts/job_creators')
326- :param dest: Destination path on remote host
288+ This is a wrapper around get_package_resource_path for backward compatibility.
327289 """
328- base_map = {'hysds' : 'mozart' , 'grq2' : 'sciflo' , 'pele' : 'sciflo' , 'mozart' : 'mozart' }
329- base = base_map .get (package_name , 'mozart' )
330-
331- # Try PyPI location first
332- pypi_path_cmd = f'python -c "import sysconfig, os; print(os.path.join(sysconfig.get_path(\' data\' ), \' share\' , \' { package_name } \' , \' { dir_relative_path } \' ))"'
333- pypi_path_result = run (pypi_path_cmd , warn_only = True )
334-
335- if pypi_path_result .succeeded :
336- pypi_path = pypi_path_result .strip ()
337- check_result = run (f'test -d { pypi_path } ' , warn_only = True )
338-
339- if check_result .succeeded :
340- run (f'cp -rp { pypi_path } { dest } ' )
341- return
342-
343- # Fallback to editable install location
344- fallback_path = f'~/{ base } /ops/{ package_name } /{ dir_relative_path } '
345- run (f'cp -rp { fallback_path } { dest } ' )
290+ src_path = get_package_resource_path (package_name , dir_relative_path , remote = True , resource_type = 'dir' )
291+ run (f'cp -rp { src_path } { dest } ' )
346292
347293
348294def resolve_files_dir (fname , files_dir ):
0 commit comments