diff --git a/.github/scripts/check_rst_syntax.py b/.github/scripts/check_rst_syntax.py new file mode 100755 index 0000000000000..92ad3f4902f2e --- /dev/null +++ b/.github/scripts/check_rst_syntax.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +"""Script to check for reStructuredText (RST) syntax in Python files. + +This script checks for RST-specific syntax patterns that should not be used +in docstrings. The project uses Markdown/MkDocs Material syntax instead. +""" + +import re +import sys +from pathlib import Path +from typing import List, Tuple + +# RST patterns to detect (pattern, description) +RST_PATTERNS: List[Tuple[str, str]] = [ + (r"\.\.\s+code-block::", "RST code-block directive (use ```language instead)"), + (r":func:", "RST :func: role (use regular Markdown links)"), + (r":class:", "RST :class: role (use regular Markdown links)"), + (r":meth:", "RST :meth: role (use regular Markdown links)"), + ( + r"\.\.\s+deprecated::", + "RST deprecated directive (use !!! deprecated admonition)", + ), + (r"\.\.\s+dropdown::", "RST dropdown directive (use ??? admonitions)"), + (r"\.\.\s+versionadded::", "RST versionadded directive"), + ( + r"\.\.\s+versionchanged::", + "RST versionchanged directive", + ), + (r"\.\.\s+warning::", "RST warning directive (use !!! warning admonition)"), + (r"\.\.\s+important::", "RST important directive (use !!! important admonition)"), + (r"\.\.\s+note::", "RST note directive (use !!! note admonition)"), + (r":private:", "RST :private: role (prefix name with underscore instead)"), + (r"`[^`]+<[^>]+>`__", "RST anonymous hyperlink (use [text](link) instead)"), + (r"`[^`]+<[^>]+>`_", "RST named hyperlink (use [text](link) instead)"), +] + + +def check_file_for_rst(file_path: Path) -> List[Tuple[int, str, str]]: + """Check a single file for RST syntax patterns. + + Args: + file_path: Path to the file to check. + + Returns: + List of tuples containing (line_number, matched_text, description). + """ + violations: List[Tuple[int, str, str]] = [] + + try: + content = file_path.read_text(encoding="utf-8") + lines = content.splitlines() + + for line_num, line in enumerate(lines, start=1): + for pattern, description in RST_PATTERNS: + matches = re.finditer(pattern, line) + for match in matches: + violations.append((line_num, match.group(0), description)) + + except (UnicodeDecodeError, PermissionError): + # Skip files that can't be read as text + pass + + return violations + + +def main() -> int: + """Main entry point for the script. + + Returns: + Exit code: 0 if no violations found, 1 if violations detected. + """ + # Get files from command line arguments + files = sys.argv[1:] + + if not files: + print("No files provided to check.", file=sys.stderr) + return 0 + + has_violations = False + + for file_str in files: + file_path = Path(file_str) + + if file_path.suffix != ".py": + # Only check Python files + continue + + if not file_path.exists(): + continue + + violations = check_file_for_rst(file_path) + + if violations: + has_violations = True + print(f"\nRST syntax detected in: {file_path}") + for line_num, matched_text, description in violations: + print(f" Line {line_num}: {matched_text}") + print(f" → {description}") + + if has_violations: + print( + "\nRST syntax is not allowed. Please use Markdown/MkDocs Material syntax instead." + ) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/check_rst_syntax.yml b/.github/workflows/check_rst_syntax.yml new file mode 100644 index 0000000000000..a89d663f40458 --- /dev/null +++ b/.github/workflows/check_rst_syntax.yml @@ -0,0 +1,55 @@ +# Check for reStructuredText (RST) syntax in Python files. +# +# This project uses Markdown/MkDocs Material syntax instead of RST. +# This workflow prevents RST-specific patterns from being introduced. + +name: "📝 Ensure no RST Syntax" + +on: + push: + branches: [master] + pull_request: + merge_group: + +# Cancel redundant workflow runs +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + check-rst-syntax: + name: "process diff" + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: "📋 Checkout Code" + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: "🐍 Setup Python" + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: "📂 Get Changed Python Files" + id: changed-files + uses: tj-actions/changed-files@v47 + with: + files: "**/*.py" + files_ignore: ".github/scripts/check_rst_syntax.py" + + - name: "🔍 Check for RST Syntax in Changed Files" + if: steps.changed-files.outputs.any_changed == 'true' + run: | + echo "Checking the following files for RST syntax:" + echo "${{ steps.changed-files.outputs.all_changed_files }}" + python .github/scripts/check_rst_syntax.py ${{ steps.changed-files.outputs.all_changed_files }} + + - name: "✅ No RST Syntax Detected" + if: steps.changed-files.outputs.any_changed != 'true' + run: | + echo "No Python files changed, skipping RST syntax check." diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6fb4e449b553e..8b922fa65efa4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,12 @@ repos: - repo: local hooks: + - id: check-rst-syntax + name: check for RST syntax + language: system + entry: python scripts/check_rst_syntax.py + files: \.py$ + types: [file] - id: core name: format and lint core language: system