Skip to content

Commit 32103d4

Browse files
only use dropin configs when option config.dropins is true
Boolean option `config.dropins` must be set to `true` so that dropin configs for the according config type are considered. So if the option config.dropins=true in enabled in local config, only local dropin configs are used (but no system or global ones).
1 parent ef2a72c commit 32103d4

File tree

2 files changed

+77
-31
lines changed

2 files changed

+77
-31
lines changed

src/west/configuration.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,31 +77,34 @@ def from_path(path: Path | None) -> '_InternalCF | None':
7777

7878
def __init__(self, path: Path):
7979
self.cp = _configparser()
80-
self.dropin_cp = _configparser()
8180
self.path = path if path.exists() else None
82-
dropin_dir = Path(f'{path}.d')
83-
self.dropin_dir = dropin_dir if dropin_dir.exists() else None
81+
if self.path:
82+
self.cp.read(self.path, encoding='utf-8')
83+
84+
# consider dropin configs
85+
self.dropin_cp = _configparser()
86+
self.dropin_dir = None
8487
self.dropin_paths = []
85-
if self.dropin_dir:
86-
# dropin configs are applied in alphabetical order
87-
for conf in sorted(self.dropin_dir.iterdir()):
88-
# only consider .conf files
89-
if conf.suffix.lower() in ['.conf', '.ini']:
90-
self.dropin_paths.append(self.dropin_dir / conf)
91-
self._read()
88+
# dropin configs must be enabled in config
89+
if self.cp.getboolean('config', 'dropins', fallback=False):
90+
# dropin dir is the config path with .d suffix
91+
dropin_dir = Path(f'{path}.d')
92+
self.dropin_dir = dropin_dir if dropin_dir.exists() else None
93+
if self.dropin_dir:
94+
# dropin configs are applied in alphabetical order
95+
for conf in sorted(self.dropin_dir.iterdir()):
96+
# only consider .conf files
97+
if conf.suffix in ['.conf', '.ini']:
98+
self.dropin_paths.append(self.dropin_dir / conf)
99+
if self.dropin_paths:
100+
self.dropin_cp.read(self.dropin_paths, encoding='utf-8')
92101

93102
def _paths(self) -> list[Path]:
94103
ret = [p for p in self.dropin_paths]
95104
if self.path:
96105
ret.append(self.path)
97106
return ret
98107

99-
def _read(self):
100-
if self.path:
101-
self.cp.read(self.path, encoding='utf-8')
102-
if self.dropin_paths:
103-
self.dropin_cp.read(self.dropin_paths, encoding='utf-8')
104-
105108
def _write(self):
106109
if not self.path:
107110
raise WestNotFound('No config file exists that can be written')

tests/test_config.py

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def test_config_list_paths_extended():
137137
{WEST_CONFIG_LOCAL}
138138
''').splitlines()
139139

140-
# create some dropins files
140+
# create some dropin config files
141141
dropin_files = [
142142
pathlib.Path(WEST_CONFIG_GLOBAL + '.d') / 'a.conf',
143143
pathlib.Path(WEST_CONFIG_GLOBAL + '.d') / 'z.conf',
@@ -150,7 +150,20 @@ def test_config_list_paths_extended():
150150
dropin_file.parent.mkdir(exist_ok=True)
151151
dropin_file.touch()
152152

153-
# list the configs
153+
# list the configs (dropin configs are not enabled by default)
154+
stdout = cmd('config --list-paths')
155+
assert stdout.splitlines() == textwrap.dedent(f'''\
156+
{WEST_CONFIG_GLOBAL}
157+
{WEST_CONFIG_SYSTEM}
158+
{WEST_CONFIG_LOCAL}
159+
''').splitlines()
160+
161+
# enable config.dropins for each config type
162+
cmd('config --local config.dropins true')
163+
cmd('config --global config.dropins true')
164+
cmd('config --system config.dropins true')
165+
166+
# list the configs (consider dropin configs)
154167
stdout = cmd('config --list-paths')
155168
assert stdout.splitlines() == textwrap.dedent(f'''\
156169
{dropin_files[0]}
@@ -164,10 +177,24 @@ def test_config_list_paths_extended():
164177
{WEST_CONFIG_LOCAL}
165178
''').splitlines()
166179

167-
# print nothing if local config does not exist (exit code 0)
168-
del os.environ['WEST_CONFIG_LOCAL']
169-
stdout = cmd('config --local --print-path')
170-
assert "" == stdout.rstrip()
180+
# list the configs only when dropin configs are disabled
181+
cmd('config --local config.dropins false')
182+
cmd('config --global config.dropins false')
183+
cmd('config --system config.dropins false')
184+
stdout = cmd('config --list-paths')
185+
assert stdout.splitlines() == textwrap.dedent(f'''\
186+
{WEST_CONFIG_GLOBAL}
187+
{WEST_CONFIG_SYSTEM}
188+
{WEST_CONFIG_LOCAL}
189+
''').splitlines()
190+
191+
# do not list any configs if no config files exist
192+
# (Note: even no local config exists, same as outside any west workspace)
193+
pathlib.Path(WEST_CONFIG_GLOBAL).unlink()
194+
pathlib.Path(WEST_CONFIG_SYSTEM).unlink()
195+
pathlib.Path(WEST_CONFIG_LOCAL).unlink()
196+
stdout = cmd('config --list-paths')
197+
assert stdout.splitlines() == []
171198

172199
def test_config_local():
173200
# test_config_system for local variables.
@@ -281,24 +308,25 @@ def test_local_creation():
281308
assert 'pytest' not in cfg(f=GLOBAL)
282309
assert cfg(f=LOCAL)['pytest']['key'] == 'val'
283310

284-
TEST_CASES_CONFIG_D = [
311+
TEST_CASES_DROPIN_CONFIGS = [
285312
# (flag, env_var)
286313
('', 'WEST_CONFIG_LOCAL'),
287314
('--local', 'WEST_CONFIG_LOCAL'),
288315
('--system', 'WEST_CONFIG_SYSTEM'),
289316
('--global', 'WEST_CONFIG_GLOBAL'),
290317
]
291318

292-
@pytest.mark.parametrize("test_case", TEST_CASES_CONFIG_D)
293-
def test_config_d_local(test_case):
319+
@pytest.mark.parametrize("test_case", TEST_CASES_DROPIN_CONFIGS)
320+
def test_config_dropins(test_case):
294321
flag, env_var = test_case
295322
config_path = pathlib.Path(os.environ[env_var])
296-
config_d_dir = pathlib.Path(f'{config_path}.d')
297-
config_d_dir.mkdir()
323+
dropin_configs_dir = pathlib.Path(f'{config_path}.d')
324+
dropin_configs_dir.mkdir()
298325

299326
# write value in actual config file
300327
cmd(f'config {flag} pytest.key val')
301328
cmd(f'config {flag} pytest.config-only val')
329+
cmd(f'config {flag} config.dropins true')
302330

303331
# read config value via command line
304332
stdout = cmd(f'config {flag} pytest.key')
@@ -314,10 +342,13 @@ def test_config_d_local(test_case):
314342
key = val
315343
config-only = val
316344
345+
[config]
346+
dropins = true
347+
317348
''')
318349

319350
# create a dropin config under .d
320-
with open(config_d_dir / 'a.conf', 'w') as conf:
351+
with open(dropin_configs_dir / 'a.conf', 'w') as conf:
321352
conf.write(textwrap.dedent('''
322353
[pytest]
323354
key = from dropin a
@@ -326,7 +357,7 @@ def test_config_d_local(test_case):
326357
'''))
327358

328359
# create a dropin config under .d
329-
with open(config_d_dir / 'z.conf', 'w') as conf:
360+
with open(dropin_configs_dir / 'z.conf', 'w') as conf:
330361
conf.write(textwrap.dedent('''
331362
[pytest]
332363
dropin-only = from dropin z
@@ -356,16 +387,20 @@ def test_config_d_local(test_case):
356387
[pytest]
357388
key = val
358389
390+
[config]
391+
dropins = true
392+
359393
''')
360394

361-
# remove config file
395+
# remove config file and only set config.dropins true again
362396
config_path.unlink()
397+
cmd(f'config {flag} config.dropins true')
363398

364399
# values from config are unset now
365400
stderr = cmd_raises(f'config {flag} pytest.config-only', subprocess.CalledProcessError)
366401
assert 'ERROR: pytest.config-only is unset' == stderr.rstrip()
367402

368-
# dropin applies now, since config does not exist
403+
# dropin config values are used now, since they are not set in config
369404
stdout = cmd(f'config {flag} pytest.key')
370405
assert 'from dropin a' == stdout.rstrip()
371406
# alphabetical order (z.conf is overwriting a.conf)
@@ -381,6 +416,14 @@ def test_config_d_local(test_case):
381416
# deletion of a value that is not existing anymore should fail
382417
cmd_raises(f'config {flag} -d pytest.config-only', subprocess.CalledProcessError)
383418

419+
# remove config (config.dropins is false by default)
420+
config_path.unlink()
421+
422+
# values from dropin configs are not considered now
423+
cmd_raises(f'config {flag} pytest.key', subprocess.CalledProcessError)
424+
cmd_raises(f'config {flag} pytest.dropin-only', subprocess.CalledProcessError)
425+
cmd_raises(f'config {flag} pytest.dropin-only-a', subprocess.CalledProcessError)
426+
cmd_raises(f'config {flag} pytest.dropin-only-z', subprocess.CalledProcessError)
384427

385428
def test_local_creation_with_topdir():
386429
# Like test_local_creation, with a specified topdir.

0 commit comments

Comments
 (0)