Skip to content

Commit 883bc9b

Browse files
support config dropin directories
config files in acccording dropin directories are considered automatically. The dropin directory is names as the config with a '.d' suffix. For example for local config the dropin directory is './west/config.d' and for global config it is '~/.westconfig.d'
1 parent 49e95ba commit 883bc9b

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

src/west/configuration.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,35 @@ def parse_key(dotted_name: str):
6868

6969
@staticmethod
7070
def from_path(path: Path | None) -> '_InternalCF | None':
71-
return _InternalCF(path) if path and path.exists() else None
71+
config = _InternalCF(path) if path and path.exists() else None
72+
if not config:
73+
return None
74+
75+
# check if dropin configs exist (in according .d directory)
76+
dropins_dir = Path(f'{path}.d')
77+
if dropins_dir.exists():
78+
# update config with values from dropin config files, whereby the
79+
# dropin configs are applied in alphabetical order
80+
for conf in sorted(dropins_dir.iterdir()):
81+
config.cp.read(dropins_dir / conf, encoding='utf-8')
82+
# finally overwrite with values from higher-prior actual config
83+
config.cp.read(path, encoding='utf-8')
84+
return config
7285

7386
def __init__(self, path: Path):
7487
self.path = path
7588
self.cp = _configparser()
89+
self._read(path)
90+
91+
def _read(self, path: Path):
7692
read_files = self.cp.read(path, encoding='utf-8')
7793
if len(read_files) != 1:
7894
raise FileNotFoundError(path)
7995

96+
def _write(self):
97+
with open(self.path, 'w', encoding='utf-8') as f:
98+
self.cp.write(f)
99+
80100
def __contains__(self, option: str) -> bool:
81101
section, key = _InternalCF.parse_key(option)
82102

@@ -110,8 +130,7 @@ def set(self, option: str, value: Any):
110130

111131
self.cp[section][key] = value
112132

113-
with open(self.path, 'w', encoding='utf-8') as f:
114-
self.cp.write(f)
133+
self._write()
115134

116135
def delete(self, option: str):
117136
section, key = _InternalCF.parse_key(option)
@@ -123,8 +142,7 @@ def delete(self, option: str):
123142
if not self.cp[section].items():
124143
del self.cp[section]
125144

126-
with open(self.path, 'w', encoding='utf-8') as f:
127-
self.cp.write(f)
145+
self._write()
128146

129147
class ConfigFile(Enum):
130148
'''Types of west configuration file.

tests/test_config.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import pathlib
88
import subprocess
9+
import textwrap
910
from typing import Any
1011

1112
import pytest
@@ -208,6 +209,40 @@ def test_local_creation():
208209
assert 'pytest' not in cfg(f=GLOBAL)
209210
assert cfg(f=LOCAL)['pytest']['key'] == 'val'
210211

212+
TEST_CASES_CONFIG_D = [
213+
# (flag, env_var)
214+
('', 'WEST_CONFIG_LOCAL'),
215+
('--local', 'WEST_CONFIG_LOCAL'),
216+
('--system', 'WEST_CONFIG_SYSTEM'),
217+
('--global', 'WEST_CONFIG_GLOBAL'),
218+
]
219+
220+
@pytest.mark.parametrize("test_case", TEST_CASES_CONFIG_D)
221+
def test_config_d_local(test_case):
222+
flag, env_var = test_case
223+
config_path = os.environ[env_var]
224+
config_d_dir = pathlib.Path(f'{config_path}.d')
225+
config_d_dir.mkdir()
226+
227+
# write value in actual config file
228+
cmd(f'config {flag} pytest.key val')
229+
stdout = cmd(f'config {flag} pytest.key')
230+
assert 'val' == stdout.rstrip()
231+
232+
# create a dropin config under .d
233+
with open(config_d_dir / 'some-file', 'w') as conf:
234+
conf.write(textwrap.dedent('''
235+
[pytest]
236+
key = from-.d
237+
another-key = from-.d
238+
'''))
239+
240+
# dropin config value is only used if option is not set in actual config
241+
stdout = cmd(f'config {flag} pytest.key')
242+
assert 'val' == stdout.rstrip()
243+
stdout = cmd(f'config {flag} pytest.another-key')
244+
assert 'from-.d' == stdout.rstrip()
245+
211246
def test_local_creation_with_topdir():
212247
# Like test_local_creation, with a specified topdir.
213248

0 commit comments

Comments
 (0)