Skip to content

Commit eac7ccc

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 510f59b commit eac7ccc

File tree

3 files changed

+115
-4
lines changed

3 files changed

+115
-4
lines changed

src/west/app/config.py

Lines changed: 25 additions & 4 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.
@@ -81,6 +81,14 @@
8181
8282
To delete <name> everywhere it's set, including the system file:
8383
west config -D <name>
84+
85+
To list the configuration files that are loaded (both the main config file
86+
and all drop-ins) in the exact order they were applied (where later values
87+
override earlier ones):
88+
west config --list-paths
89+
west config --local --list-paths
90+
west config --global --list-paths
91+
west config --system --list-paths
8492
'''
8593

8694
CONFIG_EPILOG = '''\
@@ -113,10 +121,16 @@ def do_add_parser(self, parser_adder):
113121
).add_mutually_exclusive_group()
114122

115123
group.add_argument(
116-
'-p',
117124
'--print-path',
118125
action='store_true',
119-
help='print file path from according west config(--system, --global, --local)',
126+
help='print the file path from according west '
127+
'config (--local [default], --global, --system)',
128+
)
129+
group.add_argument(
130+
'--list-paths',
131+
action='store_true',
132+
help='list all config files and dropin files that '
133+
'are currently considered by west config',
120134
)
121135
group.add_argument(
122136
'-l', '--list', action='store_true', help='list all options and their values'
@@ -172,14 +186,16 @@ def do_run(self, args, user_args):
172186
if args.list:
173187
if args.name:
174188
self.parser.error('-l cannot be combined with name argument')
175-
elif not args.name and not args.print_path:
189+
elif not any([args.name, args.print_path, args.list_paths]):
176190
self.parser.error('missing argument name (to list all options and values, use -l)')
177191
elif args.append:
178192
if args.value is None:
179193
self.parser.error('-a requires both name and value')
180194

181195
if args.print_path:
182196
self.print_path(args)
197+
elif args.list_paths:
198+
self.list_paths(args)
183199
elif args.list:
184200
self.list(args)
185201
elif delete:
@@ -196,6 +212,11 @@ def print_path(self, args):
196212
if config_path:
197213
print(config_path)
198214

215+
def list_paths(self, args):
216+
config_paths = Configuration().get_paths(args.configfile or ALL)
217+
for config_path in config_paths:
218+
print(config_path)
219+
199220
def list(self, args):
200221
what = args.configfile or ALL
201222
for option, value in self.config.items(configfile=what):

src/west/configuration.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,16 @@ def get_path(self, configfile: ConfigFile = ConfigFile.LOCAL):
193193
elif configfile == ConfigFile.GLOBAL:
194194
return self._global_path
195195

196+
def get_paths(self, configfile: ConfigFile = ConfigFile.ALL):
197+
ret = []
198+
if self._global and configfile in [ConfigFile.GLOBAL, ConfigFile.ALL]:
199+
ret += self._global._paths()
200+
if self._system and configfile in [ConfigFile.SYSTEM, ConfigFile.ALL]:
201+
ret += self._system._paths()
202+
if self._local and configfile in [ConfigFile.LOCAL, ConfigFile.ALL]:
203+
ret += self._local._paths()
204+
return ret
205+
196206
def get(
197207
self, option: str, default: str | None = None, configfile: ConfigFile = ConfigFile.ALL
198208
) -> str | None:

tests/test_config.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,86 @@ def test_config_print_path():
136136
cmd('config --system --print-path')
137137

138138

139+
TEST_CASES_CONFIG_LIST_PATHS = [
140+
# (flag, env_var)
141+
('--local', 'WEST_CONFIG_LOCAL'),
142+
('--system', 'WEST_CONFIG_SYSTEM'),
143+
('--global', 'WEST_CONFIG_GLOBAL'),
144+
]
145+
146+
147+
@pytest.mark.parametrize("test_case", TEST_CASES_CONFIG_LIST_PATHS)
148+
def test_config_list_paths(test_case):
149+
flag, env_var = test_case
150+
151+
# no config is listed (since it does not exist)
152+
stdout = cmd(f'config {flag} --list-paths')
153+
assert '' == stdout.rstrip()
154+
155+
# create the config
156+
cmd(f'config {flag} pytest.key val')
157+
158+
# check that the config is listed now
159+
stdout = cmd(f'config {flag} --list-paths')
160+
config_path = pathlib.Path(os.environ[env_var])
161+
assert f'{config_path}' == stdout.rstrip()
162+
163+
164+
def test_config_list_paths_extended():
165+
WEST_CONFIG_LOCAL = os.environ['WEST_CONFIG_LOCAL']
166+
WEST_CONFIG_GLOBAL = os.environ['WEST_CONFIG_GLOBAL']
167+
WEST_CONFIG_SYSTEM = os.environ['WEST_CONFIG_SYSTEM']
168+
169+
# create the configs
170+
cmd('config --local pytest.key val')
171+
cmd('config --global pytest.key val')
172+
cmd('config --system pytest.key val')
173+
174+
# list the configs
175+
stdout = cmd('config --list-paths')
176+
assert (
177+
stdout.splitlines()
178+
== textwrap.dedent(f'''\
179+
{WEST_CONFIG_GLOBAL}
180+
{WEST_CONFIG_SYSTEM}
181+
{WEST_CONFIG_LOCAL}
182+
''').splitlines()
183+
)
184+
185+
# create some dropins files
186+
dropin_files = [
187+
pathlib.Path(WEST_CONFIG_GLOBAL + '.d') / 'a.conf',
188+
pathlib.Path(WEST_CONFIG_GLOBAL + '.d') / 'z.conf',
189+
pathlib.Path(WEST_CONFIG_SYSTEM + '.d') / 'a.conf',
190+
pathlib.Path(WEST_CONFIG_SYSTEM + '.d') / 'z.conf',
191+
pathlib.Path(WEST_CONFIG_LOCAL + '.d') / 'a.conf',
192+
pathlib.Path(WEST_CONFIG_LOCAL + '.d') / 'z.conf',
193+
]
194+
for dropin_file in dropin_files:
195+
dropin_file.parent.mkdir(exist_ok=True)
196+
dropin_file.touch()
197+
198+
# list the configs
199+
stdout = cmd('config --list-paths')
200+
assert (
201+
stdout.splitlines()
202+
== textwrap.dedent(f'''\
203+
{dropin_files[0]}
204+
{dropin_files[1]}
205+
{WEST_CONFIG_GLOBAL}
206+
{dropin_files[2]}
207+
{dropin_files[3]}
208+
{WEST_CONFIG_SYSTEM}
209+
{dropin_files[4]}
210+
{dropin_files[5]}
211+
{WEST_CONFIG_LOCAL}
212+
''').splitlines()
213+
)
214+
215+
# print nothing if local config does not exist (exit code 0)
216+
del os.environ['WEST_CONFIG_LOCAL']
217+
218+
139219
def test_config_local():
140220
# test_config_system for local variables.
141221
cmd('config --local pytest.local foo')

0 commit comments

Comments
 (0)