Skip to content

Commit a2a79e4

Browse files
authored
🐛 Validate field default ordering in oneline config (#63)
### Problem The oneline parser maps comma-separated values to `needs_fields` strictly by position (index). When a field **without** a default is defined after a field **with** a default, it becomes unreachable if the user omits the optional fields — exactly like Python's `SyntaxError: parameter without a default follows parameter with a default`. Example invalid configuration: ```toml needs_fields = [ { "name" = "id" }, { "name" = "implements", "type" = "list[str]", "default" = [] }, { "name" = "type", "default" = "impl" }, { "name" = "title" }, # required, but comes after optional fields ] ``` With this config, `@need MY_ID, My Title` maps `My Title` to `implements` (index 1) instead of `title` (index 3), and `title` is silently skipped. ### Solution Add `check_fields_default_order()` validation to `OneLineCommentStyle` that rejects configurations where a field without a default follows a field with a default. This is checked alongside existing schema, required-field, and uniqueness validations in `check_fields_configuration()`. ### Changes - `config.py`: New `check_fields_default_order()` method, integrated into `check_fields_configuration()` - `test_analyse_config.py`: Negative test case exercising the validation
1 parent d30193c commit a2a79e4

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

src/sphinx_codelinks/config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,29 @@ def check_fields_mutually_exclusive(self) -> list[str]:
276276
needs_field_names.add(_field["name"])
277277
return errors
278278

279+
def check_fields_default_order(self) -> list[str]:
280+
errors = []
281+
seen_default = False
282+
first_default_field = ""
283+
for _field in self.needs_fields:
284+
has_default = _field.get("default") is not None
285+
if has_default and not seen_default:
286+
seen_default = True
287+
first_default_field = _field["name"]
288+
elif not has_default and seen_default:
289+
errors.append(
290+
f"Field '{_field['name']}' without a default follows "
291+
f"field '{first_default_field}' which has a default. "
292+
f"Fields without defaults must be defined before fields with defaults."
293+
)
294+
return errors
295+
279296
def check_fields_configuration(self) -> list[str]:
280297
return (
281298
self.check_schema()
282299
+ self.check_required_fields()
283300
+ self.check_fields_mutually_exclusive()
301+
+ self.check_fields_default_order()
284302
)
285303

286304
def get_cnt_required_fields(self) -> int:

tests/test_analyse_config.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,23 @@ def test_config_schema_validator_negative(analyse_config, result):
152152
"Missing required fields: ['title', 'type']",
153153
],
154154
),
155+
(
156+
OneLineCommentStyle(
157+
start_sequence="@need",
158+
end_sequence="\n",
159+
field_split_char=",",
160+
needs_fields=[
161+
{"name": "id"},
162+
{"name": "implements", "type": "list[str]", "default": []},
163+
{"name": "type", "default": "impl"},
164+
{"name": "title"}, # required after optional
165+
],
166+
),
167+
[
168+
"Field 'title' without a default follows field 'implements' which has a default. "
169+
"Fields without defaults must be defined before fields with defaults.",
170+
],
171+
),
155172
],
156173
)
157174
def test_oneline_schema_validator_negative(oneline_config, result):

0 commit comments

Comments
 (0)