Skip to content

Commit 04435fb

Browse files
committed
Fixes rst_to_trlc parser
- Strip triling sphinx filter suffixes - Consume multi-line directive arg lines before field list parsing
1 parent 2c03ee6 commit 04435fb

2 files changed

Lines changed: 72 additions & 2 deletions

File tree

‎bazel/rules/rules_score/src/rst_to_trlc.py‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
_RE_MARKUP = re.compile(r"\*\*?(.*?)\*\*?")
8484
_RE_DIRECTIVE = re.compile(r"^\.\.\s+([\w]+)::\s*(.*)")
8585
_RE_FIELD = re.compile(r"^\s+:([\w]+):\s*(.*)") # noqa: E501
86+
_RE_NEEDS_FILTER = re.compile(r"(\s*\[[^\[\]]*\])+\s*$")
8687

8788
_TRLC_HEADER = """\
8889
/********************************************************************************
@@ -136,8 +137,19 @@ def _escape(text: str) -> str:
136137

137138

138139
def _collect_refs(fields: dict[str, str]) -> list[str]:
139-
"""Extract all cross-reference IDs from relationship fields."""
140-
return [r.strip() for k in _REF_FIELDS if k in fields for r in fields[k].split(",") if r.strip()]
140+
"""Extract all cross-reference IDs from relationship fields.
141+
142+
Strips one or more trailing sphinx-needs filter expressions (e.g.
143+
``[version==1]`` or ``[version==1][status==valid]``), which are not
144+
valid TRLC reference syntax.
145+
"""
146+
return [
147+
_RE_NEEDS_FILTER.sub("", r.strip()).strip()
148+
for k in _REF_FIELDS
149+
if k in fields
150+
for r in fields[k].split(",")
151+
if r.strip()
152+
]
141153

142154

143155
def parse_directives(content: str) -> list[dict[str, Any]]:
@@ -154,6 +166,14 @@ def parse_directives(content: str) -> list[dict[str, Any]]:
154166
directive, title = m.group(1), m.group(2).strip()
155167
i += 1
156168

169+
# RST directive arguments may wrap onto continuation lines (docutils
170+
# allows this); consume any such lines here before the field list,
171+
# otherwise they get mis-parsed as body text and the real :id: (and
172+
# other fields) on the following lines are missed entirely.
173+
while i < len(lines) and lines[i].strip() and not _RE_FIELD.match(lines[i]):
174+
title += " " + lines[i].strip()
175+
i += 1
176+
157177
fields, i = _collect_fields(lines, i)
158178
raw_body, i = _collect_body(lines, i)
159179
body = _RE_MARKUP.sub(r"\1", raw_body).strip()

‎bazel/rules/rules_score/test/rst_to_trlc_test.py‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,37 @@ def test_strips_rst_bold_markup_from_body(self):
336336
self.assertNotIn("**", result[0]["body"])
337337
self.assertIn("robust", result[0]["body"])
338338

339+
def test_multi_line_title_is_joined_and_fields_still_parsed(self):
340+
"""docutils allows a directive's argument to wrap onto continuation
341+
lines with no blank-line separator; the real field list must still
342+
be found on the lines that follow."""
343+
rst = _rst(
344+
".. comp_req:: Logging During the C++ Static Storage",
345+
" construction and destruction.",
346+
" :id: comp_req__test__001",
347+
" :safety: QM",
348+
"",
349+
" Body.",
350+
)
351+
result = parse_directives(rst)
352+
self.assertEqual(len(result), 1)
353+
self.assertEqual(
354+
result[0]["title"],
355+
"Logging During the C++ Static Storage construction and destruction.",
356+
)
357+
self.assertEqual(result[0]["fields"]["id"], "comp_req__test__001")
358+
359+
def test_single_line_title_unaffected_by_continuation_handling(self):
360+
rst = _rst(
361+
".. feat_req:: Single Line Title",
362+
" :id: feat_req__test__001",
363+
" :safety: QM",
364+
"",
365+
" Body.",
366+
)
367+
result = parse_directives(rst)
368+
self.assertEqual(result[0]["title"], "Single Line Title")
369+
339370

340371
# ---------------------------------------------------------------------------
341372
# render_trlc – TRLC output for each S-CORE type
@@ -545,6 +576,25 @@ def test_mitigates_is_not_a_ref_field(self):
545576
def test_returns_empty_when_no_ref_fields(self):
546577
self.assertEqual(_collect_refs({"safety": "QM", "reqtype": "Functional"}), [])
547578

579+
def test_strips_trailing_needs_filter(self):
580+
"""sphinx-needs filter suffixes are not valid TRLC reference syntax."""
581+
self.assertEqual(
582+
_collect_refs({"satisfies": "req_001[version==1]"}),
583+
["req_001"],
584+
)
585+
586+
def test_strips_multiple_trailing_needs_filters(self):
587+
self.assertEqual(
588+
_collect_refs({"satisfies": "req_001[version==1][status==valid]"}),
589+
["req_001"],
590+
)
591+
592+
def test_strips_needs_filter_from_each_comma_separated_ref(self):
593+
self.assertEqual(
594+
_collect_refs({"satisfies": "req_001[version==1], req_002[status==valid]"}),
595+
["req_001", "req_002"],
596+
)
597+
548598

549599
# ---------------------------------------------------------------------------
550600
# _collect_body – blank-line handling consistency

0 commit comments

Comments
 (0)