Add test for RST heading underline lengths, closes #2544

Added test_rst_heading_underlines_match_title_length() to verify that RST
heading underlines match their title lengths. The test properly handles:
- Overline+underline style headings (skips validation for those)
- Empty lines before underlines (ignores them)
- Minimum 5-character underline length (avoids false positives)

Running this test identified 14 heading underline mismatches which have
been fixed across 5 documentation files:
- docs/authentication.rst (3 headings)
- docs/plugin_hooks.rst (4 headings)
- docs/internals.rst (5 headings)
- docs/deploying.rst (1 heading)
- docs/changelog.rst (1 heading)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Simon Willison 2025-10-26 09:44:58 -07:00
commit 4fe1765dc3
6 changed files with 69 additions and 14 deletions

View file

@ -106,6 +106,61 @@ def test_functions_marked_with_documented_are_documented(documented_fns, fn):
assert fn.__name__ in documented_fns
def test_rst_heading_underlines_match_title_length():
"""Test that RST heading underlines are the same length as their titles."""
# Common RST underline characters
underline_chars = ['-', '=', '~', '^', '+', '*', '#']
errors = []
for rst_file in docs_path.glob("*.rst"):
content = rst_file.read_text()
lines = content.split('\n')
for i in range(len(lines) - 1):
current_line = lines[i]
next_line = lines[i + 1]
# Check if next line is entirely made of a single underline character
# and is at least 5 characters long (to avoid false positives)
if (next_line and
len(next_line) >= 5 and
len(set(next_line)) == 1 and
next_line[0] in underline_chars):
# Skip if the previous line is empty (blank line before underline)
if not current_line:
continue
# Check if this is an overline+underline style heading
# Look at the line before current_line to see if it's also an underline
if i > 0:
prev_line = lines[i - 1]
if (prev_line and
len(prev_line) >= 5 and
len(set(prev_line)) == 1 and
prev_line[0] in underline_chars and
len(prev_line) == len(next_line)):
# This is overline+underline style, skip it
continue
# This is a heading underline
title_length = len(current_line)
underline_length = len(next_line)
if title_length != underline_length:
errors.append(
f"{rst_file.name}:{i+1}: Title length {title_length} != underline length {underline_length}\n"
f" Title: {current_line!r}\n"
f" Underline: {next_line!r}"
)
if errors:
raise AssertionError(
f"Found {len(errors)} RST heading(s) with mismatched underline length:\n\n" +
"\n\n".join(errors)
)
# Tests for testing_plugins.rst documentation
# fmt: off