Coverage for torxtools/cfgtools.py: 60%
19 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 22:02 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 22:02 +0000
1"""
2Functions for working with configuration file selection and reading.
4The :func:`which` convenience function returns the first file that should be used as configuration file from a list.
5"""
7import os
8import typing as t
10from boltons.iterutils import first, flatten_iter, unique_iter
11from xdg import XDG_CONFIG_DIRS
13from . import xdgtools
15__all__ = [
16 "which",
17 "candidates",
18]
21# pylint: disable=redefined-outer-name
22def which(
23 cfgfile: str = None,
24 candidates: t.List[str] = None,
25 default: str = "/dev/null",
26 verify: t.Callable = os.path.exists,
27) -> t.Optional[str]:
28 """
29 Convenience function to return the configuration file to read.
31 Parameters
32 ----------
33 cfgfile: str, None
34 a single path. If not None, then it will be returned without calling the verify function.
36 candidates: t.List[str], None
37 a list of paths. If cfgfile is None, then the verify function will be
38 called for each of these paths and the first one for which verify
39 returns True will be returned.
41 If no file is matched, then :file:`/dev/null` will be returned.
43 verify: t.Callable
44 callable that will be called for every file in candidate. Defaults to os.path.exists.
46 default: str, None
47 value to be returned if cfgfile is None and no match from candidates was found
49 Returns
50 -------
51 str:
52 cfgfile, a value from candidates, or default
54 Example
55 -------
57 .. code-block:: python
59 import argparse, sys
60 from torxtools import cfgtools, pathtools
62 defaults = pathtools.expandpath(["~/.my-cfgfile"], ["/etc/cfgfile"])
64 parser = argparse.ArgumentParser()
65 parser.add_argument("--cfgfile")
66 args = parser.parse_args(sys.argv[:1])
68 print(cfgtools.which(args.cfgfile, defaults))
70 """
71 if not callable(verify): 71 ↛ 72line 71 didn't jump to line 72 because the condition on line 71 was never true
72 raise TypeError(f"expected verify to be a callable, not: {verify}")
74 if cfgfile:
75 return cfgfile
76 return first(candidates or [], default=default, key=verify)
79def candidates(cfgname: str = None) -> t.List[str]:
80 """
81 Convenience function to return list of candidates paths for a configuration file.
83 Parameters
84 ----------
85 cfgfile: str, None
86 a single path. If not None, then it will be returned without calling the verify function.
88 Returns
89 -------
90 list[str]:
91 list of paths that are candidates
93 """
95 # fmt: off
96 # pylint: disable=invalid-name
97 _CFGFILE_SEARCH_PATHS: t.List[str] = list(
98 unique_iter(
99 flatten_iter(
100 [
101 "./.{cfgname}", # ./.flasket.yml
102 "./{cfgname}", # ./flasket.yml
103 "$XDG_CONFIG_HOME/{cfgname}", # ~/.config/flasket.yml
104 "~/.{cfgname}", # ~/.flasket.yml
105 [os.path.join(str(e), "{cfgname}") for e in XDG_CONFIG_DIRS],
106 "/etc/{cfgname}", # /etc/flasket.yml
107 ],
108 ),
109 ),
110 )
111 # fmt: on
112 #
113 # Set the environment to ensure paths are calculated.
114 xdgtools.setenv()
116 search_paths = _CFGFILE_SEARCH_PATHS
117 if cfgname:
118 search_paths = [e.format(cfgname=cfgname) for e in search_paths]
119 return search_paths