Skip to content

Commit 7f4b2fc

Browse files
authored
Unknown/invalid option names in pyproject.toml file will now cause fatal error (#151)
1 parent d74c55b commit 7f4b2fc

File tree

5 files changed

+66
-3
lines changed

5 files changed

+66
-3
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
## Unreleased
44

5+
### Fixes
6+
- Invalid option names in pyproject.toml file will now stop Robotidy (with optional hint message) [#150](https://github.com/MarketSquare/robotframework-tidy/issues/150)
7+
58
### Features
69
- Allow to use spaces in pyproject.toml configuration file [#148](https://github.com/MarketSquare/robotframework-tidy/issues/148)
710

11+
812
## 1.3.0
913

1014
### Transformers

robotidy/cli.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,28 @@ def load_toml_file(path: str) -> Dict[str, Any]:
140140

141141

142142
def read_pyproject_config(path: str) -> Dict[str, Any]:
143-
click.echo(f'Reading {path}')
144143
config = load_toml_file(path)
145144
config = config.get("tool", {}).get("robotidy", {})
146145
return {k.replace('--', '').replace('-', '_'): v for k, v in config.items()}
147146

148147

148+
def parse_opt(opt):
149+
while opt and opt[0] == '-':
150+
opt = opt[1:]
151+
return opt.replace('-', '_')
152+
153+
154+
def validate_config_options(params, config):
155+
if params is None:
156+
return
157+
allowed = {parse_opt(opt) for param in params for opt in param.opts}
158+
for conf in config:
159+
if conf not in allowed:
160+
rec_finder = RecommendationFinder()
161+
similar = rec_finder.find(conf, list(allowed))
162+
raise click.NoSuchOption(conf, possibilities=similar)
163+
164+
149165
def read_config(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Optional[str]:
150166
# if --config was not used, try to find pyproject.toml or robotidy.toml file
151167
if value:
@@ -161,7 +177,7 @@ def read_config(ctx: click.Context, param: click.Parameter, value: Optional[str]
161177
k: str(v) if not isinstance(v, (list, dict)) else v
162178
for k, v in config.items()
163179
}
164-
180+
validate_config_options(ctx.command.params, config)
165181
default_map: Dict[str, Any] = {}
166182
if ctx.default_map:
167183
default_map.update(ctx.default_map)

tests/utest/test_cli.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from unittest.mock import MagicMock, Mock
55
import pytest
6-
from click import FileError
6+
from click import FileError, NoSuchOption
77

88
from .utils import run_tidy, save_tmp_model
99
from robotidy.cli import (
@@ -142,6 +142,7 @@ def test_read_pyproject_config_e2e(self):
142142
config_path = str(Path(Path(__file__).parent, 'testdata', 'only_pyproject'))
143143
ctx_mock = MagicMock()
144144
ctx_mock.params = {'src': [config_path]}
145+
ctx_mock.command.params = None
145146
param_mock = Mock()
146147
read_config(ctx_mock, param_mock, value=None)
147148
assert ctx_mock.default_map == expected_parsed_config
@@ -152,6 +153,19 @@ def test_read_invalid_config(self):
152153
read_pyproject_config(config_path)
153154
assert 'Error reading configuration file: ' in str(err)
154155

156+
@pytest.mark.parametrize('option, correct', [
157+
('confgure', 'configure'),
158+
('idontexist', None)
159+
])
160+
def test_read_invalid_option_config(self, option, correct):
161+
config_path = str(Path(Path(__file__).parent, 'testdata', 'invalid_options_config', f'pyproject_{option}.toml'))
162+
ctx_mock = MagicMock()
163+
param_mock = MagicMock()
164+
with pytest.raises(NoSuchOption) as err:
165+
read_config(ctx_mock, param_mock, config_path)
166+
similar = '' if correct is None else f' Did you mean {correct}'
167+
assert f'no such option: {option}{similar}'
168+
155169
def test_read_config_from_param(self):
156170
expected_parsed_config = {
157171
'overwrite': 'False',
@@ -164,6 +178,7 @@ def test_read_config_from_param(self):
164178
}
165179
config_path = str(Path(Path(__file__).parent, 'testdata', 'robotidy.toml'))
166180
ctx_mock = MagicMock()
181+
ctx_mock.command.params = None
167182
param_mock = Mock()
168183
read_config(ctx_mock, param_mock, config_path)
169184
assert ctx_mock.default_map == expected_parsed_config
@@ -181,6 +196,7 @@ def test_read_config_without_param(self):
181196
config_path = str(Path(Path(__file__).parent, 'testdata', 'robotidy.toml'))
182197
ctx_mock = MagicMock()
183198
ctx_mock.params = {'src': [config_path]}
199+
ctx_mock.command.params = None
184200
param_mock = Mock()
185201
read_config(ctx_mock, param_mock, value=None)
186202
assert ctx_mock.default_map == expected_parsed_config
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[tool.robotidy]
2+
overwrite = false
3+
diff = false
4+
startline = 10
5+
endline = 20
6+
transform = [
7+
"DiscardEmptySections:allow_only_comments=True",
8+
"SplitTooLongLine"
9+
]
10+
confgure = [
11+
"DiscardEmptySections:allow_only_comments=False",
12+
"OrderSettings: keyword_before = documentation,tags,timeout,arguments"
13+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[tool.robotidy]
2+
idontexist = 10
3+
overwrite = false
4+
diff = false
5+
startline = 10
6+
endline = 20
7+
transform = [
8+
"DiscardEmptySections:allow_only_comments=True",
9+
"SplitTooLongLine"
10+
]
11+
configure = [
12+
"DiscardEmptySections:allow_only_comments=False",
13+
"OrderSettings: keyword_before = documentation,tags,timeout,arguments"
14+
]

0 commit comments

Comments
 (0)