Skip to content

Commit e112404

Browse files
authored
Merge pull request #302 from viccie30/yamlfix-toml-configuration
Add support for yamlfix.toml configuration files
2 parents 8813081 + 0858453 commit e112404

File tree

4 files changed

+156
-13
lines changed

4 files changed

+156
-13
lines changed

docs/index.md

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,15 @@ variable, use `fix_code`:
9292

9393
`yamlfix` uses the `maison` library to find and parse configuration from standard locations, and can additionally be configured through environment variables.
9494

95-
Any configuration found in the [YamlfixConfig class](./reference/#yamlfix.model.YamlfixConfig) can be set through your projects `pyproject.toml`, a custom `toml`-file or through the environment by providing an environment variable like `{yamlfix_env_prefix}_{yamlfix_config_key}`.
95+
Any configuration found in the [YamlfixConfig class](./reference/#yamlfix.model.YamlfixConfig) can be set through your projects' `pyproject.toml`, a `.yamlfix.toml` file, a custom `toml`-file or through the environment by providing an environment variable like `{yamlfix_env_prefix}_{yamlfix_config_key}`.
9696

9797
Configuration options that are provided through environment variables have higher priority than options provided through configuration files and will override those keys.
9898

9999
All provided [configuration options](#configuration-options), be it through `pyproject.toml`, config-files or env-vars, will be parsed by `pydantic`, so the target value type (str, bool, int, etc.) will be enforced, even if the provided value has the wrong type (for example all env vars in linux systems are strings, but pydantic will parse them to bools/numbers where necessary).
100100

101-
## Auto-Configure through `pyproject.toml`
101+
## Auto-Configure through `pyproject.toml` or `.yamlfix.toml`
102102

103-
The `maison` library will automatically pick up your `yamlfix` configuration through your projects `pyproject.toml`. It will look in the section named `tool.yamlfix` and apply the provided [configuration options](#configuration-options). For example:
103+
The `maison` library will automatically pick up your `yamlfix` configuration through your projects' `pyproject.toml`. It will look in the section named `tool.yamlfix` and apply the provided [configuration options](#configuration-options). For example:
104104

105105
```toml
106106
# pyproject.toml
@@ -111,15 +111,57 @@ line_length = 80
111111
none_representation = "null"
112112
```
113113

114-
## Provide config-files
114+
When running `yamlfix` as a standalone cli application it might be desireable to provide a config file containing just the configuration related to `yamlfix`. The `maison` library will therefore also read `.yamlfix.toml` and `yamlfix.toml` if they exist. No section headers are necessary for these configuration files, as the expected behaviour is, that those files contain only configuration related to `yamlfix`. For example:
115115

116-
When running `yamlfix` as a standalone cli application it might be desireable to provide a config file containing just the configuration related to `yamlfix`. A cli-argument `-c` (`--config-file`) can be provided multiple times to read configuration values from `toml` formatted files. The rightmost value-files override the value-files preceding them, only trumped by environment variables. No section headers are necessary for these configuration files, as the expected behaviour is, that those files contain only configuration related to `yamlfix`. For example:
116+
```toml
117+
# .yamlfix.toml
118+
allow_duplicate_keys = true
119+
line_length = 80
120+
none_representation = "null"
121+
```
122+
123+
The configurations from `pyproject.toml`, `yamlfix.toml`, and `.yamlfix.toml` are merged from left to right. This means that settings in `yamlfix.toml` override those in `pyproject.toml` and settings in `.yamlfix.toml` override those in `pyproject.toml` and `yamlfix.toml`. Environment variables override all settings set in configuration files. For example:
124+
125+
```toml
126+
# pyproject.toml
127+
allow_duplicate_keys = false
128+
line_length = 100
129+
preserve_quotes = false
130+
```
131+
132+
```toml
133+
# yamlfix.toml
134+
allow_duplicate_keys = true
135+
preserve_quotes = true
136+
```
137+
138+
```toml
139+
# .yamlfix.toml
140+
preserve_quotes = false
141+
```
142+
143+
These provided configuration files would result in a merged runtime-configuration of:
144+
```toml
145+
# merged configuration
146+
allow_duplicate_keys = true
147+
line_length = 100
148+
preserve_quotes = false
149+
```
150+
151+
## Custom config-files
152+
153+
A cli-argument `-c` (`--config-file`) can be provided multiple times to read configuration values from `toml` formatted files. If `-c` is provided, the default configuration files are ignored. The rightmost value-files override the value-files preceding them, only trumped by environment variables. No section headers are necessary for these configuration files, as the expected behaviour is, that those files contain only configuration related to `yamlfix`. For example:
117154

118155
```bash
119156
# run yamlfix with two config files
120157
yamlfix -c base.toml --config-file environment.toml file.yaml
121158
```
122159

160+
```toml
161+
# .yamlfix.toml
162+
preserve_quotes = true
163+
```
164+
123165
```toml
124166
# base.toml
125167
allow_duplicate_keys = false
@@ -206,13 +248,13 @@ Environment variable override:
206248
export YAMLFIX_WHITELINES="0"
207249
```
208250

209-
This option allows to keep a speficied number of whitelines between lines.
251+
This option allows to keep a speficied number of whitelines between lines.
210252

211-
It's useful if, for one, you like to separate GitHub Actions job steps or Docker-Compose
253+
It's useful if, for one, you like to separate GitHub Actions job steps or Docker-Compose
212254
service definitions with a blank line.
213255

214-
Bear in mind that, like **Comments Whitelines**, it won't insert whitelines if there's no
215-
whitelines in the first place. It will only fix 1 or more whitelines to the desired
256+
Bear in mind that, like **Comments Whitelines**, it won't insert whitelines if there's no
257+
whitelines in the first place. It will only fix 1 or more whitelines to the desired
216258
amount (or remove them completely by default).
217259

218260
### Comments Whitelines
@@ -268,7 +310,7 @@ Environment variable override:
268310
export YAMLFIX_CONFIG_PATH="/etc/yamlfix/"
269311
```
270312

271-
Configure the base config-path that `maison` will look for a `pyproject.toml` configuration file. This path is traversed upwards until such a file is found.
313+
Configure the base config-path that `maison` will look for a `pyproject.toml`, `.yamlfix.toml`, or `yamlfix.toml` configuration file. This path is traversed upwards until such a file is found.
272314

273315
### Explicit Document Start
274316

src/yamlfix/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ def configure_yamlfix(
2222
if config_path_env:
2323
config_path = Path(config_path_env)
2424

25+
if not config_files:
26+
config_files = [
27+
"pyproject.toml",
28+
"yamlfix.toml",
29+
".yamlfix.toml",
30+
]
31+
2532
config: UserConfig = UserConfig(
2633
schema=YamlfixConfig,
2734
merge_configs=True,

tests/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
11
"""Store the classes and fixtures used throughout the tests."""
2+
3+
import os
4+
5+
import pytest
6+
7+
8+
@pytest.fixture(autouse=True)
9+
def _unset_yamlfix_config_path() -> None:
10+
"""Unset YAMLFIX_CONFIG_PATH environment variable."""
11+
os.environ.pop("YAMLFIX_CONFIG_PATH", None)

tests/e2e/test_cli.py

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,81 @@ def test_check_one_file_no_changes(runner: CliRunner, tmp_path: Path) -> None:
215215
assert test_file.read_text() == test_file_source
216216

217217

218-
def test_config_parsing(runner: CliRunner, tmp_path: Path) -> None:
218+
def test_default_config_parsing(runner: CliRunner, tmp_path: Path) -> None:
219+
"""Default configuration files are parsed, merged, and applied correctly."""
220+
os.environ["YAMLFIX_CONFIG_PATH"] = str(tmp_path)
221+
pyproject_config = dedent(
222+
"""\
223+
[tool.yamlfix]
224+
line_length = 90
225+
quote_basic_values = "true"
226+
quote_representation = "'"
227+
none_represtation = ""
228+
"""
229+
)
230+
pyproject_config_file = tmp_path / "pyproject.toml"
231+
pyproject_config_file.write_text(pyproject_config)
232+
233+
nodot_toml_config = dedent(
234+
"""\
235+
none_representation = "null"
236+
quote_representation = '"'
237+
"""
238+
)
239+
nodot_toml_config_file = tmp_path / "yamlfix.toml"
240+
nodot_toml_config_file.write_text(nodot_toml_config)
241+
242+
dot_toml_config = dedent(
243+
"""\
244+
none_representation = "~"
245+
"""
246+
)
247+
dot_toml_config_file = tmp_path / ".yamlfix.toml"
248+
dot_toml_config_file.write_text(dot_toml_config)
249+
250+
test_source = dedent(
251+
f"""\
252+
---
253+
really_long_string: >
254+
{("abcdefghij " * 10).strip()}
255+
single_quoted_string: 'value1'
256+
double_quoted_string: "value2"
257+
unquoted_string: value3
258+
none_value:
259+
none_value2: ~
260+
none_value3: null
261+
none_value4: NULL
262+
"""
263+
)
264+
test_source_file = tmp_path / "source.yaml"
265+
test_source_file.write_text(test_source)
266+
267+
result = runner.invoke(
268+
cli,
269+
[
270+
str(test_source_file),
271+
],
272+
)
273+
274+
assert result.exit_code == 0
275+
assert test_source_file.read_text() == dedent(
276+
f"""\
277+
---
278+
really_long_string: >
279+
{("abcdefghij " * 9).strip()}
280+
abcdefghij
281+
single_quoted_string: "value1"
282+
double_quoted_string: "value2"
283+
unquoted_string: "value3"
284+
none_value: ~
285+
none_value2: ~
286+
none_value3: ~
287+
none_value4: ~
288+
"""
289+
)
290+
291+
292+
def test_custom_config_parsing(runner: CliRunner, tmp_path: Path) -> None:
219293
"""Provided config options are parsed, merged, and applied correctly."""
220294
os.environ["YAMLFIX_CONFIG_PATH"] = str(tmp_path)
221295
pyproject_config = dedent(
@@ -227,13 +301,14 @@ def test_config_parsing(runner: CliRunner, tmp_path: Path) -> None:
227301
)
228302
pyproject_config_file = tmp_path / "pyproject.toml"
229303
pyproject_config_file.write_text(pyproject_config)
304+
230305
toml_config = dedent(
231306
"""\
232307
none_representation = "null"
233308
quote_representation = '"'
234309
"""
235310
)
236-
toml_config_file = tmp_path / "yamlfix.toml"
311+
toml_config_file = tmp_path / "custom.toml"
237312
toml_config_file.write_text(toml_config)
238313

239314
# the ini config is currenlty parsed incorrectly and it is not possible to provide
@@ -247,8 +322,9 @@ def test_config_parsing(runner: CliRunner, tmp_path: Path) -> None:
247322
none_representation = "~"
248323
"""
249324
)
250-
ini_config_file = tmp_path / "yamlfix.ini"
325+
ini_config_file = tmp_path / "custom.ini"
251326
ini_config_file.write_text(ini_config)
327+
252328
test_source = dedent(
253329
f"""\
254330
---
@@ -266,6 +342,14 @@ def test_config_parsing(runner: CliRunner, tmp_path: Path) -> None:
266342
test_source_file = tmp_path / "source.yaml"
267343
test_source_file.write_text(test_source)
268344

345+
ignored_config = dedent(
346+
"""\
347+
quote_keys_and_basic_values = true
348+
"""
349+
)
350+
ignored_config_file = tmp_path / ".yamlfix.toml"
351+
ignored_config_file.write_text(ignored_config)
352+
269353
# we have to provide the pyproject.toml as a relative path to YAMLFIX_CONFIG_PATH
270354
# until this is fixed: https://github.com/dbatten5/maison/issues/141
271355
pyproject_config_file_name = "pyproject.toml"

0 commit comments

Comments
 (0)