Skip to content

Commit c29931f

Browse files
add support for 'config --list-paths'
added config command to list all config files and dropin files which are currently considered by west.
1 parent 3572a90 commit c29931f

File tree

3 files changed

+119
-10
lines changed

3 files changed

+119
-10
lines changed

src/west/app/config.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import argparse
88

99
from west.commands import CommandError, WestCommand
10-
from west.configuration import ConfigFile
10+
from west.configuration import ConfigFile, Configuration
1111

1212
CONFIG_DESCRIPTION = '''\
1313
West configuration file handling.
@@ -74,13 +74,22 @@
7474
To delete <name> everywhere it's set, including the system file:
7575
west config -D <name>
7676
77-
Additionally to the config file a dropin config directory is considered.
78-
The directory is named as the according config file, but with a '.d' suffix.
79-
As a result there are three levels for dropin config directories (local, global
80-
and system), whereby all '.conf' files from each dropin directory are loaded in
81-
alphabetical order.
77+
For each configuration type (local, global, and system), an additional
78+
drop-in directory is supported. This directory is named after the configuration
79+
file with a `.d` suffix.
80+
81+
All files inside a drop-in directory must use `.conf` extension and are
82+
loaded in **alphabetical order**.
8283
For example:
8384
.west/config.d/basics.conf
85+
86+
To list the configuration files that are loaded (both the main config file
87+
and all drop-ins) in the exact order they were applied (where later values
88+
override earlier ones):
89+
west config --list-paths
90+
west config --local --list-paths
91+
west config --global --list-paths
92+
west config --system --list-paths
8493
'''
8594

8695
CONFIG_EPILOG = '''\
@@ -113,9 +122,12 @@ def do_add_parser(self, parser_adder):
113122
"action to perform (give at most one)"
114123
).add_mutually_exclusive_group()
115124

116-
group.add_argument('-p', '--print-path', action='store_true',
117-
help='print file path from according west config'
118-
'(--system, --global, --local)')
125+
group.add_argument('--print-path', action='store_true',
126+
help='print the file path from according west '
127+
'config (--local [default], --global, --system)')
128+
group.add_argument('--list-paths', action='store_true',
129+
help='list all config files and dropin files that '
130+
'are currently considered by west config')
119131
group.add_argument('-l', '--list', action='store_true',
120132
help='list all options and their values')
121133
group.add_argument('-d', '--delete', action='store_true',
@@ -151,7 +163,7 @@ def do_run(self, args, user_args):
151163
if args.list:
152164
if args.name:
153165
self.parser.error('-l cannot be combined with name argument')
154-
elif not args.name and not args.print_path:
166+
elif not any([args.name, args.print_path, args.list_paths]):
155167
self.parser.error('missing argument name '
156168
'(to list all options and values, use -l)')
157169
elif args.append:
@@ -160,6 +172,8 @@ def do_run(self, args, user_args):
160172

161173
if args.print_path:
162174
self.print_path(args)
175+
elif args.list_paths:
176+
self.list_paths(args)
163177
elif args.list:
164178
self.list(args)
165179
elif delete:
@@ -176,6 +190,11 @@ def print_path(self, args):
176190
if config_path:
177191
print(config_path)
178192

193+
def list_paths(self, args):
194+
config_paths = Configuration().get_paths(args.configfile or ALL)
195+
for config_path in config_paths:
196+
print(config_path)
197+
179198
def list(self, args):
180199
what = args.configfile or ALL
181200
for option, value in self.config.items(configfile=what):

src/west/configuration.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ def __init__(self, path: Path):
9090
self.dropin_paths.append(self.dropin_dir / conf)
9191
self._read()
9292

93+
def _paths(self) -> list[Path]:
94+
ret = [p for p in self.dropin_paths]
95+
if self.path:
96+
ret.append(self.path)
97+
return ret
98+
9399
def _read(self):
94100
if self.path:
95101
self.cp.read(self.path, encoding='utf-8')
@@ -211,6 +217,16 @@ def get_path(self, configfile: ConfigFile = ConfigFile.LOCAL):
211217
elif configfile == ConfigFile.GLOBAL:
212218
return self._global_path
213219

220+
def get_paths(self, configfile: ConfigFile = ConfigFile.ALL):
221+
ret = []
222+
if self._global and configfile in [ConfigFile.GLOBAL, ConfigFile.ALL]:
223+
ret += self._global._paths()
224+
if self._system and configfile in [ConfigFile.SYSTEM, ConfigFile.ALL]:
225+
ret += self._system._paths()
226+
if self._local and configfile in [ConfigFile.LOCAL, ConfigFile.ALL]:
227+
ret += self._local._paths()
228+
return ret
229+
214230
def get(self, option: str,
215231
default: str | None = None,
216232
configfile: ConfigFile = ConfigFile.ALL) -> str | None:

tests/test_config.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,80 @@ def test_config_print_path():
9393
stdout = cmd('config --system --print-path')
9494
assert os.environ["WEST_CONFIG_SYSTEM"] == stdout.rstrip()
9595

96+
# print nothing if local config does not exist (exit code 0)
97+
del os.environ['WEST_CONFIG_LOCAL']
98+
stdout = cmd('config --local --print-path')
99+
assert "" == stdout.rstrip()
100+
101+
TEST_CASES_CONFIG_LIST_PATHS = [
102+
# (flag, env_var)
103+
('--local', 'WEST_CONFIG_LOCAL'),
104+
('--system', 'WEST_CONFIG_SYSTEM'),
105+
('--global', 'WEST_CONFIG_GLOBAL'),
106+
]
107+
108+
@pytest.mark.parametrize("test_case", TEST_CASES_CONFIG_LIST_PATHS)
109+
def test_config_list_paths(test_case):
110+
flag, env_var = test_case
111+
112+
# no config is listed (since it does not exist)
113+
stdout = cmd(f'config {flag} --list-paths')
114+
assert '' == stdout.rstrip()
115+
116+
# create the config
117+
cmd(f'config {flag} pytest.key val')
118+
119+
# check that the config is listed now
120+
stdout = cmd(f'config {flag} --list-paths')
121+
config_path = pathlib.Path(os.environ[env_var])
122+
assert f'{config_path}' == stdout.rstrip()
123+
124+
def test_config_list_paths_extended():
125+
WEST_CONFIG_LOCAL = os.environ['WEST_CONFIG_LOCAL']
126+
WEST_CONFIG_GLOBAL = os.environ['WEST_CONFIG_GLOBAL']
127+
WEST_CONFIG_SYSTEM = os.environ['WEST_CONFIG_SYSTEM']
128+
129+
# create the configs
130+
cmd('config --local pytest.key val')
131+
cmd('config --global pytest.key val')
132+
cmd('config --system pytest.key val')
133+
134+
# list the configs
135+
stdout = cmd('config --list-paths')
136+
assert stdout == textwrap.dedent(f'''\
137+
{WEST_CONFIG_GLOBAL}
138+
{WEST_CONFIG_SYSTEM}
139+
{WEST_CONFIG_LOCAL}
140+
''')
141+
142+
# create some dropins files
143+
dropin_files = [
144+
pathlib.Path(WEST_CONFIG_GLOBAL + '.d') / 'a.conf',
145+
pathlib.Path(WEST_CONFIG_GLOBAL + '.d') / 'z.conf',
146+
pathlib.Path(WEST_CONFIG_SYSTEM + '.d') / 'a.conf',
147+
pathlib.Path(WEST_CONFIG_SYSTEM + '.d') / 'z.conf',
148+
pathlib.Path(WEST_CONFIG_LOCAL + '.d') / 'a.conf',
149+
pathlib.Path(WEST_CONFIG_LOCAL + '.d') / 'z.conf',
150+
]
151+
for dropin_file in dropin_files:
152+
dropin_file.parent.mkdir(exist_ok=True)
153+
dropin_file.touch()
154+
155+
# list the configs
156+
stdout = cmd('config --list-paths')
157+
assert stdout == textwrap.dedent(f'''\
158+
{dropin_files[0]}
159+
{dropin_files[1]}
160+
{WEST_CONFIG_GLOBAL}
161+
{dropin_files[2]}
162+
{dropin_files[3]}
163+
{WEST_CONFIG_SYSTEM}
164+
{dropin_files[4]}
165+
{dropin_files[5]}
166+
{WEST_CONFIG_LOCAL}
167+
''')
168+
169+
# print nothing if local config does not exist (exit code 0)
96170
del os.environ['WEST_CONFIG_LOCAL']
97171
stdout = cmd('config --local --print-path')
98172
assert "" == stdout.rstrip()

0 commit comments

Comments
 (0)