diff --git a/.chronus/changes/fix-sphinx-required-after-code-block-2026-6-9-15-20-0.md b/.chronus/changes/fix-sphinx-required-after-code-block-2026-6-9-15-20-0.md new file mode 100644 index 00000000000..cf2ec9852d7 --- /dev/null +++ b/.chronus/changes/fix-sphinx-required-after-code-block-2026-6-9-15-20-0.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-client-python" +--- + +Fix Sphinx docstring rendering when a `Required.` (or other) annotation followed a code block. The annotation is now inserted into the prose before the code block instead of being appended after it. diff --git a/packages/http-client-python/generator/pygen/codegen/models/utils.py b/packages/http-client-python/generator/pygen/codegen/models/utils.py index 82e80b85577..46aa53f749c 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/utils.py +++ b/packages/http-client-python/generator/pygen/codegen/models/utils.py @@ -11,10 +11,20 @@ OrderedSet = dict[T, None] +CODE_BLOCK_MARKER = ".. code-block::" + + def add_to_description(description: str, entry: str) -> str: - if description: - return f"{description} {entry}" - return entry + if not description: + return entry + # When the description contains a code block, the entry (e.g. "Required.") must be + # inserted into the prose *before* the code block. Appending it after the code block + # (e.g. "]. Required.") leaves it dangling at the end of the rendered block and breaks + # Sphinx rendering. + if CODE_BLOCK_MARKER in description: + prose, _, code_block = description.partition(CODE_BLOCK_MARKER) + return f"{prose.rstrip()} {entry}\n\n{CODE_BLOCK_MARKER}{code_block}" + return f"{description} {entry}" def add_to_pylint_disable(curr_str: str, entry: str) -> str: @@ -34,6 +44,10 @@ class NamespaceType(str, Enum): LOCALS_LENGTH_LIMIT = 25 -REQUEST_BUILDER_BODY_VARIABLES_LENGTH = 6 # how many body variables are present in a request builder +REQUEST_BUILDER_BODY_VARIABLES_LENGTH = ( + 6 # how many body variables are present in a request builder +) -OPERATION_BODY_VARIABLES_LENGTH = 14 # how many body variables are present in an operation +OPERATION_BODY_VARIABLES_LENGTH = ( + 14 # how many body variables are present in an operation +) diff --git a/packages/http-client-python/tests/unit/test_add_to_description.py b/packages/http-client-python/tests/unit/test_add_to_description.py new file mode 100644 index 00000000000..c6d1ed781d0 --- /dev/null +++ b/packages/http-client-python/tests/unit/test_add_to_description.py @@ -0,0 +1,35 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +"""Tests for add_to_description code-block handling.""" +from pygen.codegen.models.utils import add_to_description + + +def test_add_to_description_empty() -> None: + assert add_to_description("", "Required.") == "Required." + + +def test_add_to_description_plain() -> None: + assert add_to_description("The tools.", "Required.") == "The tools. Required." + + +def test_add_to_description_inserts_before_code_block() -> None: + description = "The tools to use.\n\n.. code-block:: json\n\n [\n 1\n ]" + result = add_to_description(description, "Required.") + assert result == ( + "The tools to use. Required.\n\n.. code-block:: json\n\n [\n 1\n ]" + ) + # The annotation must not be appended after the closing of the code block. + assert not result.rstrip().endswith("Required.") + + +def test_add_to_description_multiple_code_blocks_inserts_before_first() -> None: + description = ( + "Prose.\n\n.. code-block:: json\n\n [1]\n\n.. code-block:: json\n\n [2]" + ) + result = add_to_description(description, "Required.") + assert result == ( + "Prose. Required.\n\n.. code-block:: json\n\n [1]\n\n.. code-block:: json\n\n [2]" + )