Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions poetry.lock

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (other): This lock file was regenerated with a newer Poetry (2.2.1 → 2.4.1), which rewrote environment markers across many packages unrelated to the intended change (charset-normalizer, distro, jiter, jsonpatch, langchain-core, openai, pyyaml, requests, tenacity, urllib3, uuid_utils, zstandard, etc.). The only intended lock change for this PR is promoting docstring-parser into the test group. Consider regenerating the lock with the same Poetry version the repo standardizes on so the diff is limited to docstring-parser; the unrelated marker churn creates merge friction and makes it hard to see the real change. Please confirm these marker rewrites are benign and expected.

🤖 Generated by Astra

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ grpcio = { version = ">=1.80.0,<2.0.0", optional = true }

[tool.poetry.group.test.dependencies]
pytest = "^8.4.0"
docstring-parser = ">=0.17.0"
coverage = "^7.9.2"
pytest-cov = "^6.0.0"
pytest-xdist = "^3.7.0"
Expand Down
162 changes: 162 additions & 0 deletions tests/test_create_docs_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""Tests for MDX helper functions in scripts/create_docs.py.

Covers _escape_curly_braces, _convert_rst_code_blocks, and _sanitize_description —
the helpers that fix MDX brace escaping and RST code block conversion in generated docs.
"""

from __future__ import annotations

import sys
import os
Comment thread
fercor-cisco marked this conversation as resolved.

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))

from create_docs import _escape_curly_braces, _convert_rst_code_blocks, _sanitize_description
Comment thread
etserend marked this conversation as resolved.
Comment on lines +9 to +14

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 minor (other): Two lint issues in this import block that will fail the ruff pre-commit hook (contradicting the "CI green" claim):

  1. E402 (already flagged in an existing unresolved review): the from create_docs import ... after sys.path.insert(...) is a module-level import not at top of file. E4 is selected in pyproject.toml and E402 is not in the tests/**/*.py ignore list. It is not auto-fixable, so the hook fails. tests/conftest.py handles the same pattern with # noqa: E402.

  2. I001 (import sorting): import sys precedes import os, but isort (I is selected, not ignored for tests) wants alphabetical order (os before sys). This is auto-fixable, but the hook will still report a modification.

Suggested change
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))
from create_docs import _escape_curly_braces, _convert_rst_code_blocks, _sanitize_description
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))
from create_docs import _escape_curly_braces, _convert_rst_code_blocks, _sanitize_description # noqa: E402

🤖 Generated by Astra



class TestEscapeCurlyBraces:
"""Tests for _escape_curly_braces — escapes { } outside fenced/inline code for MDX."""

def test_plain_text_unchanged(self):
"""Text with no braces is returned as-is."""
assert _escape_curly_braces("hello world") == "hello world"

def test_bare_braces_escaped(self):
"""Bare braces in prose are escaped."""
assert _escape_curly_braces("foo {bar} baz") == r"foo \{bar\} baz"

def test_inline_code_not_escaped(self):
"""Braces inside inline code spans are left alone."""
assert _escape_curly_braces("use `{key}` here") == "use `{key}` here"

def test_fenced_code_block_not_escaped(self):
"""Braces inside a fenced code block are left alone."""
text = '```python\nd = {"key": 1}\n```'
assert _escape_curly_braces(text) == text

def test_braces_outside_fenced_block_escaped(self):
"""Only braces outside fenced blocks are escaped."""
text = "Before {x}.\n```python\n{y}\n```\nAfter {z}."
result = _escape_curly_braces(text)
assert r"\{x\}" in result
assert "{y}" in result # inside fence — untouched
assert r"\{z\}" in result

def test_two_adjacent_fenced_blocks_with_braces_between(self):
"""Braces between two fenced blocks are escaped; content inside each is not."""
text = "```python\n{a}\n```\n{between}\n```python\n{b}\n```"
result = _escape_curly_braces(text)
assert "{a}" in result
assert r"\{between\}" in result
assert "{b}" in result

def test_multiple_inline_code_spans(self):
"""Multiple inline code spans on one line — braces inside each left alone."""
result = _escape_curly_braces("use `{a}` and `{b}` but escape {c}")
assert "`{a}`" in result
assert "`{b}`" in result
assert r"\{c\}" in result

def test_inline_and_prose_braces_on_same_line(self):
"""Inline code braces and prose braces mixed on one line."""
result = _escape_curly_braces("prefix {p} `mid {m}` suffix {s}")
assert r"\{p\}" in result
assert "`mid {m}`" in result
assert r"\{s\}" in result

def test_empty_string(self):
"""Empty input returns empty string."""
assert _escape_curly_braces("") == ""


class TestConvertRstCodeBlocks:
"""Tests for _convert_rst_code_blocks — converts RST :: blocks to fenced ```python."""

def test_no_rst_blocks_unchanged(self):
"""Text without :: markers is returned as-is."""
text = "Regular paragraph.\n\nAnother paragraph."
assert _convert_rst_code_blocks(text) == text

def test_basic_rst_block_converted(self):
"""A :: paragraph produces a fenced python block with de-indented code."""
text = "Example::\n\n result = foo()\n return result\n"
result = _convert_rst_code_blocks(text)
assert "```python" in result
assert "result = foo()" in result
assert "return result" in result
assert "::" not in result

def test_standalone_double_colon_removed(self):
"""A standalone :: line is removed; blank separator line appears inside the fence."""
text = "::\n\n code_here()\n"
result = _convert_rst_code_blocks(text)
assert result == "\n```python\n\ncode_here()\n```"

def test_trailing_blank_lines_stripped_from_block(self):
"""Trailing blank lines after the block content are not emitted inside the fence."""
text = "Example::\n\n foo()\n\n\n"
result = _convert_rst_code_blocks(text)
assert result == "Example\n```python\n\nfoo()\n```"

def test_indentation_stripped(self):
"""Four-space indentation is removed from block lines."""
text = "Example::\n\n indented_line()\n"
result = _convert_rst_code_blocks(text)
assert " indented_line()" not in result
assert "indented_line()" in result

def test_prose_after_block_preserved(self):
"""Lines after the RST block (unindented) continue to appear correctly."""
text = "Example::\n\n foo()\n\nMore prose here."
result = _convert_rst_code_blocks(text)
assert "```python" in result
assert "foo()" in result
assert "More prose here." in result
lines = result.split("\n")
fence_close = lines.index("```")
prose_idx = lines.index("More prose here.")
assert prose_idx > fence_close

def test_prose_ending_in_double_colon_no_indented_block(self):
"""Prose line ending in :: with no indented block following loses the :: but is otherwise intact."""
text = "Refer to namespace::\n\nNext paragraph (not indented)."
result = _convert_rst_code_blocks(text)
assert "```" not in result
assert "Refer to namespace" in result
assert "Next paragraph (not indented)." in result

def test_double_colon_mid_line_not_treated_as_rst(self):
""":: mid-line (e.g. namespace::method) does not trigger RST block conversion."""
text = "Use namespace::method() here.\n\nMore text."
assert _convert_rst_code_blocks(text) == text

def test_empty_string(self):
"""Empty input returns empty string."""
assert _convert_rst_code_blocks("") == ""


class TestSanitizeDescription:
"""Tests for _sanitize_description — RST conversion then brace escaping."""

def test_rst_and_braces_both_handled(self):
"""RST block is fenced first; braces inside the fence are preserved, outside are escaped."""
text = "Set config using {value}.\n\nExample::\n\n config({key: val})\n"
result = _sanitize_description(text)
assert r"\{value\}" in result # prose brace escaped
assert "```python" in result # RST block converted
assert "{key: val}" in result # brace inside fence left alone

def test_ordering_rst_before_braces(self):
"""_convert_rst_code_blocks runs before _escape_curly_braces so fenced braces are safe."""
text = "Example::\n\n d = {1: 2}\n"
result = _sanitize_description(text)
assert "{1: 2}" in result # inside fence — must NOT be escaped
assert r"\{1" not in result

def test_plain_text_passthrough(self):
"""Plain text with no RST or braces is unchanged."""
assert _sanitize_description("simple text") == "simple text"

def test_empty_string(self):
"""Empty input returns empty string."""
assert _sanitize_description("") == ""
Loading