Coverage for python / lsst / ctrl / execute / findPackageFile.py: 24%
19 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-13 01:44 -0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-13 01:44 -0800
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/>.
28import os
29import sys
31import lsst.utils
32from lsst.resources import ResourcePath
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.
38 Parameters
39 ----------
40 filename : `str`
41 The unqualified name of a file to locate.
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.
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.
51 Returns
52 -------
53 `lsst.resources.ResourcePath`
55 Raises
56 ------
57 FileNotFoundError
58 If a requested file object cannot be located in the candidate hierarchy
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
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
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