DOC: Polish MyST cross-references (bases, re-exports, page labels)#1824
Open
romanlutz wants to merge 5 commits into
Open
DOC: Polish MyST cross-references (bases, re-exports, page labels)#1824romanlutz wants to merge 5 commits into
romanlutz wants to merge 5 commits into
Conversation
When PR microsoft#1782 converted Sphinx reST roles to plain double-backticks under jupyter-book 2's MyST renderer, all the symbol cross-references in our docstrings stopped being clickable. This restores them without forcing contributors to learn MyST link syntax. Changes: * �uild_scripts/gen_api_md.py now emits an explicit, FQN-scoped MyST label before every class, function, and method heading (e.g. (api-pyrit_prompt_target-PromptTarget)=) and post-processes every docstring text, parameter description, return description, and raises description: backtick code spans whose contents unambiguously resolve to a PyRIT class, function, or method become MyST links to the right anchor. Ambiguous short names and fenced code blocks are left alone. The API index page now links each preview symbol to its anchor too. * Cleaned up 13 leftover Sphinx reST roles in pyrit/ that PR microsoft#1782 missed (cli_helpers, scorer_metrics, pyrit_scan, tree_of_attacks). * Added �uild_scripts/check_no_rest_roles.py plus a pre-commit hook so newly introduced :class: / :func: / :meth: / etc. roles are rejected before landing. * Updated the style guide to describe the auto-linker behaviour and point at the new pre-commit guard. * 21 new unit tests in ests/unit/build_scripts/ cover the rewriter (single/double backticks, Class.method, FQN, current-class context, ambiguous skip, fenced-block protection, existing-link idempotency, tilde/dot prefix) and the pre-commit guard. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two extra unit tests on the auto-linker integration path: * `test_process_docstring_text_protects_doctest_examples` pins the order I had to swap mid-implementation: `_escape_docstring_examples` must run before `_rewrite_symbol_refs` so a known PyRIT symbol appearing inside a `>>>` doctest example stays as raw text (otherwise the code sample would render as broken markdown). * `test_render_function_emits_anchor_and_links_docstring_fields` plus `test_render_function_uses_method_anchor_when_class_name_given` are end-to-end smoke tests on `render_function`: they assert the `(api-...)=` label is emitted with the right scoping (module vs. method) and that every docstring field (text, params, returns, raises) goes through the rewriter so a regression in any of those four code paths fails the build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extends the docstring auto-linker added in the parent PR with three small follow-ups so generated API pages cross-reference more aggressively: 1. `Bases:` lines run each base through the symbol rewriter, so PyRIT bases become MyST links while external bases (`str`, `Enum`) stay as plain code spans. 2. `## Re-exports` entries link both the alias name (preferring the alias's own module-qualified path) and the target FQN. Unresolvable targets fall back to plain code spans, matching the rest of the docs. 3. Each module page emits a `label: api-pyrit_<module_slug>` frontmatter field so cross-page references like `[](#api-pyrit_prompt_target)` target the page itself. Placing the label inline before the H1 doesn't work because MyST consumes the H1 as the page title, so the frontmatter field is the only reliable way to bind a page-level anchor. 16 new unit tests in tests/unit/build_scripts/test_gen_api_md.py cover each path, including the external-base, unresolvable-target, and nested module-slug edge cases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…icks The `check-no-rest-roles` pre-commit hook added in microsoft#1823 flags four `:meth:model_dump` / `:meth:model_validate` references in `pyrit/models/conversation_reference.py` and `pyrit/models/retry_event.py` that landed via PR microsoft#1769 before the hook existed. Replace them with plain double-backticks so the hook passes cleanly on this stacked branch and the deprecation notices render as readable code spans under MyST instead of literal `:meth: ame` text. `model_dump` / `model_validate` are Pydantic methods, not PyRIT API, so the auto-linker leaves them as plain code spans (correct: there is nothing in our docs to link them to). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacks on #1823.
Extends the docstring auto-linker added in the parent PR with three small follow-ups, plus a small cleanup that the parent PR's
check-no-rest-roleshook turned up.Before / after
Same section —
pyrit.prompt_target.AzureBlobStorageTargetheading and Bases line — in the rendered API page. Before, thePromptTargetbase is a plain pink code span; after, it's an underlined, clickable MyST link that jumps straight to thePromptTargetdefinition further down the page. External bases likestrandEnum(not shown in this crop, but visible elsewhere on the page) correctly stay as plain code spans because they're not in the symbol index.Before (

origin/romanlutz/myst-cross-refs-audit):After (this PR):

Auto-linker extensions
Bases:line is linked.render_classnow emits each base individually through a new_format_bases()helper that wraps it in a single backtick and runs it through_rewrite_symbol_refs. PyRIT bases become MyST links; external bases (str,Enum, etc.) stay as plain code spans because they're not in the symbol index.## Re-exportssection is linked on both sides._format_reexport_alias()prefers the module-qualified path lookup (mod_name.alias_name) so an alias resolves to its OWN page rather than the canonical definition; falls back to the short-name rewriter when no FQN entry exists._format_reexport_target()runs the target through_rewrite_symbol_refsso resolvable FQNs link and external paths stay plain._resolve_aliases()rewrites every alias to a real class/function beforerender_moduleruns, so the## Re-exportsblock is dead in current production builds. The linking is in place defensively for any future code path that emits aliases, and is fully exercised by the new unit tests.Page-level label as a frontmatter field. Each module page emits
label: api-pyrit_<module_slug>in the frontmatter so cross-page references like[](#api-pyrit_prompt_target)resolve to the page itself.(api-pyrit_<slug>)=before the H1, but MyST consumes the H1 as the page title and discards any label placed in the body before it (verified by inspectingapi.pyrit-prompt-target.json— no page-level label was registered). The frontmatterlabel:field is the only reliable way to bind a page-level anchor. Confirmed it registers as{"identifier":"api-pyrit_prompt_target","kind":"page","url":"/api/pyrit-prompt-target"}inmyst.xref.json.Cleanup
Replaces 4 stray
:meth:reST roles inpyrit/models/conversation_reference.pyandpyrit/models/retry_event.pywith plain double-backticks. These landed via #1769 before thecheck-no-rest-roleshook from #1823 existed; they slipped past the hook because they were already in the tree.model_dump/model_validateare Pydantic API, not PyRIT, so the auto-linker leaves them as plain code spans (correct: nothing in our docs to link to).Tests
16 new unit tests in
tests/unit/build_scripts/test_gen_api_md.py(34 total now). Coverage:Full unit suite: 8165 passed, 119 skipped in 2:19 (unchanged).
Validation
make pre-commit— all hooks pass (includingcheck-no-rest-roles)make unit-test— 8165 passed, 119 skippedmake docs-build— zero new warnings vs the parent branch baseline (same 69 unique warnings, all pre-existinggrid gutter/jupytextfrontmatter notices)api/pyrit-prompt-target/index.html:Bases:is a real<a href="#api-pyrit-prompt-target-prompttarget">link for PyRIT bases and plain<code>str</code>, <code>Enum</code>for external onesKnown pre-existing issue not fixed here
uv run ty check tests/unitflags_rewrite_symbol_refs(None, {}) is None # type: ignore[arg-type]ontests/unit/build_scripts/test_gen_api_md.py:243. The# type: ignorecomment is mypy-style andtydoesn't honor it yet. This already exists on the parent branch and isn't addressable here.