Coverage for python / lsst / ctrl / execute / findPackageFile.py: 24%

19 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-15 06:47 -0300

1# This file is part of ctrl_execute. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <https://www.gnu.org/licenses/>. 

27 

28import os 

29import sys 

30 

31import lsst.utils 

32from lsst.resources import ResourcePath 

33 

34 

35def find_package_file(filename: str, kind: str = "config", platform: str | None = None) -> ResourcePath: 

36 """Find a package file from a set of candidate locations. 

37 

38 Parameters 

39 ---------- 

40 filename : `str` 

41 The unqualified name of a file to locate. 

42 

43 kind : `str` 

44 The name of a subdirectory in which to look for the file within a 

45 package location, relative to an ``etc/`` directory. 

46 

47 platform : `str` | `None` 

48 The name of a platform plugin in which to look for the file, or `None` 

49 if no platform plugin should be searched. 

50 

51 Returns 

52 ------- 

53 `lsst.resources.ResourcePath` 

54 

55 Raises 

56 ------ 

57 FileNotFoundError 

58 If a requested file object cannot be located in the candidate hierarchy 

59 

60 Notes 

61 ----- 

62 The candidate locations are, in descending order of preference: 

63 - An ``.lsst`` directory in the user's home directory. 

64 - An ``lsst`` directory in the user's ``$XDG_CONFIG_HOME`` directory 

65 - An ``etc/{kind}`` directory in the EUPS stack environment for the 

66 platform. 

67 - An ``etc/{kind}`` directory in the current Python environment/venv shared 

68 data directory. 

69 - An ``etc/{kind}`` directory in an installed ``lsst.ctrl.platform.*`` 

70 package. 

71 - An ``etc/{kind}`` directory in the ``lsst.ctrl.execute`` package. 

72 """ 

73 # If the path, after expansion, is absolute, we don't need to go looking 

74 # for it, it should be exactly where it is. 

75 if (_filename := ResourcePath(filename, forceAbsolute=False)).isabs(): 

76 return _filename 

77 

78 home_dir = os.getenv("HOME", "~") 

79 xdg_config_home = os.getenv("XDG_CONFIG_HOME", "~/.config") 

80 try: 

81 platform_pkg_dir = lsst.utils.getPackageDir(f"ctrl_platform_{platform}") 

82 except (LookupError, ValueError): 

83 platform_pkg_dir = None 

84 

85 file_candidates = [ 

86 ResourcePath(home_dir).join(".lsst").join(_filename), 

87 ResourcePath(xdg_config_home).join("lsst").join(_filename), 

88 (ResourcePath(platform_pkg_dir).join("etc").join(kind).join(_filename) if platform_pkg_dir else None), 

89 ResourcePath(sys.exec_prefix).join("etc").join(kind).join(_filename), 

90 ( 

91 ResourcePath(f"resource://lsst.ctrl.platform.{platform}/etc/{kind}/{_filename}") 

92 if platform 

93 else None 

94 ), 

95 ResourcePath(f"resource://lsst.ctrl.execute/etc/{kind}/{_filename}"), 

96 ] 

97 try: 

98 found_file: ResourcePath = [c for c in file_candidates if c is not None and c.exists()][0] 

99 except IndexError: 

100 raise FileNotFoundError(f"No file {filename} found in package file lookup") 

101 return found_file