From f3b22ff45a7e50d51ac41f12947899b14ed0fb3e Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Fri, 4 Sep 2026 19:21:42 +0000 Subject: [PATCH 1/6] docs(bigquery): fix table rendering in markdown docs --- packages/google-cloud-bigquery/docs/conf.py | 22 +++++++++++++++- .../tests/unit/test_docs.py | 26 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 packages/google-cloud-bigquery/tests/unit/test_docs.py diff --git a/packages/google-cloud-bigquery/docs/conf.py b/packages/google-cloud-bigquery/docs/conf.py index df1c18b68e31..73a888980481 100644 --- a/packages/google-cloud-bigquery/docs/conf.py +++ b/packages/google-cloud-bigquery/docs/conf.py @@ -24,9 +24,9 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os import shlex +import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -37,6 +37,26 @@ # See also: https://github.com/docascode/sphinx-docfx-yaml/issues/85 sys.path.insert(0, os.path.abspath(".")) +# sphinx-markdown-builder unconditionally inserts trailing newlines into +# table cell paragraphs, breaking DevSite table formatting. Suppressing +# newlines while inside table cells preserves valid GFM tables. +try: + import sphinx_markdown_builder.markdown_writer as _smb_writer + + _orig_depart_paragraph = _smb_writer.MarkdownTranslator.depart_paragraph + + def _table_safe_depart_paragraph(self, node): + if getattr(self, "table_entries", None): + return + return _orig_depart_paragraph(self, node) + + _smb_writer.MarkdownTranslator.depart_paragraph = _table_safe_depart_paragraph + _smb_writer.MarkdownTranslator.depart_compact_paragraph = ( + _table_safe_depart_paragraph + ) +except ImportError: + pass + __version__ = "" # -- General configuration ------------------------------------------------ diff --git a/packages/google-cloud-bigquery/tests/unit/test_docs.py b/packages/google-cloud-bigquery/tests/unit/test_docs.py new file mode 100644 index 000000000000..b861fe04e54d --- /dev/null +++ b/packages/google-cloud-bigquery/tests/unit/test_docs.py @@ -0,0 +1,26 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pathlib +import runpy + + +def test_docs_conf_executes_successfully(): + docs_dir = pathlib.Path(__file__).parent.parent.parent / "docs" + conf_path = docs_dir / "conf.py" + + res = runpy.run_path(str(conf_path)) + + assert "project" in res + assert res["project"] == "google-cloud-bigquery" From 319959b387c84d1f749e8ee7b0e4cda756748cc5 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Fri, 4 Sep 2026 12:39:25 -0700 Subject: [PATCH 2/6] Update packages/google-cloud-bigquery/tests/unit/test_docs.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- packages/google-cloud-bigquery/tests/unit/test_docs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/google-cloud-bigquery/tests/unit/test_docs.py b/packages/google-cloud-bigquery/tests/unit/test_docs.py index b861fe04e54d..994dd3849e3b 100644 --- a/packages/google-cloud-bigquery/tests/unit/test_docs.py +++ b/packages/google-cloud-bigquery/tests/unit/test_docs.py @@ -20,6 +20,10 @@ def test_docs_conf_executes_successfully(): docs_dir = pathlib.Path(__file__).parent.parent.parent / "docs" conf_path = docs_dir / "conf.py" + if not conf_path.exists(): + import pytest + pytest.skip("docs/conf.py not found") + res = runpy.run_path(str(conf_path)) assert "project" in res From 34eb197d07e4e853a216a72da54565f78e7e0be0 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Fri, 4 Sep 2026 21:45:31 +0000 Subject: [PATCH 3/6] style: format test_docs.py with black --- packages/google-cloud-bigquery/tests/unit/test_docs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/google-cloud-bigquery/tests/unit/test_docs.py b/packages/google-cloud-bigquery/tests/unit/test_docs.py index 994dd3849e3b..48f185da8b14 100644 --- a/packages/google-cloud-bigquery/tests/unit/test_docs.py +++ b/packages/google-cloud-bigquery/tests/unit/test_docs.py @@ -22,6 +22,7 @@ def test_docs_conf_executes_successfully(): if not conf_path.exists(): import pytest + pytest.skip("docs/conf.py not found") res = runpy.run_path(str(conf_path)) From 71354a73fd52c079261750036ba78dd0534c6625 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Thu, 10 Sep 2026 18:32:59 +0000 Subject: [PATCH 4/6] docs: add TODO(b/559711363) in conf.py --- packages/google-cloud-bigquery/docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/google-cloud-bigquery/docs/conf.py b/packages/google-cloud-bigquery/docs/conf.py index 73a888980481..471ecd830796 100644 --- a/packages/google-cloud-bigquery/docs/conf.py +++ b/packages/google-cloud-bigquery/docs/conf.py @@ -37,6 +37,7 @@ # See also: https://github.com/docascode/sphinx-docfx-yaml/issues/85 sys.path.insert(0, os.path.abspath(".")) +# TODO(b/559711363): Apply this table formatting fix across all google-cloud-* libraries. # sphinx-markdown-builder unconditionally inserts trailing newlines into # table cell paragraphs, breaking DevSite table formatting. Suppressing # newlines while inside table cells preserves valid GFM tables. From 36c8df2ced918218fa2501001bd7324a3e8965bf Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Thu, 10 Sep 2026 18:33:51 +0000 Subject: [PATCH 5/6] test: add unit test for markdown translator monkeypatch --- .../tests/unit/test_docs.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/google-cloud-bigquery/tests/unit/test_docs.py b/packages/google-cloud-bigquery/tests/unit/test_docs.py index 48f185da8b14..8d253a520830 100644 --- a/packages/google-cloud-bigquery/tests/unit/test_docs.py +++ b/packages/google-cloud-bigquery/tests/unit/test_docs.py @@ -14,6 +14,9 @@ import pathlib import runpy +import sys +import types +from unittest import mock def test_docs_conf_executes_successfully(): @@ -29,3 +32,60 @@ def test_docs_conf_executes_successfully(): assert "project" in res assert res["project"] == "google-cloud-bigquery" + + +def test_docs_conf_patches_markdown_translator(): + docs_dir = pathlib.Path(__file__).parent.parent.parent / "docs" + conf_path = docs_dir / "conf.py" + + if not conf_path.exists(): + import pytest + + pytest.skip("docs/conf.py not found") + + mock_orig_depart = mock.MagicMock(return_value="original_output") + + class FakeTranslator: + depart_paragraph = mock_orig_depart + depart_compact_paragraph = mock_orig_depart + + fake_module = types.ModuleType("sphinx_markdown_builder.markdown_writer") + fake_module.MarkdownTranslator = FakeTranslator + + with mock.patch.dict( + sys.modules, + { + "sphinx_markdown_builder": types.ModuleType("sphinx_markdown_builder"), + "sphinx_markdown_builder.markdown_writer": fake_module, + }, + ): + runpy.run_path(str(conf_path)) + + assert FakeTranslator.depart_paragraph is not mock_orig_depart + assert FakeTranslator.depart_compact_paragraph is not mock_orig_depart + + translator_in_table = FakeTranslator() + translator_in_table.table_entries = ["entry"] + assert ( + FakeTranslator.depart_paragraph(translator_in_table, mock.MagicMock()) is None + ) + assert ( + FakeTranslator.depart_compact_paragraph(translator_in_table, mock.MagicMock()) + is None + ) + mock_orig_depart.assert_not_called() + + translator_outside_table = FakeTranslator() + mock_node = mock.MagicMock() + assert ( + FakeTranslator.depart_paragraph(translator_outside_table, mock_node) + == "original_output" + ) + mock_orig_depart.assert_called_once_with(translator_outside_table, mock_node) + + mock_orig_depart.reset_mock() + assert ( + FakeTranslator.depart_compact_paragraph(translator_outside_table, mock_node) + == "original_output" + ) + mock_orig_depart.assert_called_once_with(translator_outside_table, mock_node) From 74a624607b4eda3a7ccb9aa8926ae4a3aee06d88 Mon Sep 17 00:00:00 2001 From: Shuowei Li Date: Thu, 10 Sep 2026 18:35:24 +0000 Subject: [PATCH 6/6] docs(bigquery): shorten TODO and refine tests per style guide --- packages/google-cloud-bigquery/docs/conf.py | 5 +- .../tests/unit/test_docs.py | 70 ++++++++++--------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/packages/google-cloud-bigquery/docs/conf.py b/packages/google-cloud-bigquery/docs/conf.py index 471ecd830796..4b940ce04693 100644 --- a/packages/google-cloud-bigquery/docs/conf.py +++ b/packages/google-cloud-bigquery/docs/conf.py @@ -37,10 +37,7 @@ # See also: https://github.com/docascode/sphinx-docfx-yaml/issues/85 sys.path.insert(0, os.path.abspath(".")) -# TODO(b/559711363): Apply this table formatting fix across all google-cloud-* libraries. -# sphinx-markdown-builder unconditionally inserts trailing newlines into -# table cell paragraphs, breaking DevSite table formatting. Suppressing -# newlines while inside table cells preserves valid GFM tables. +# TODO(b/559711363): Propagate table formatting fix across all google-cloud-* libraries. try: import sphinx_markdown_builder.markdown_writer as _smb_writer diff --git a/packages/google-cloud-bigquery/tests/unit/test_docs.py b/packages/google-cloud-bigquery/tests/unit/test_docs.py index 8d253a520830..4fd1f9f76a87 100644 --- a/packages/google-cloud-bigquery/tests/unit/test_docs.py +++ b/packages/google-cloud-bigquery/tests/unit/test_docs.py @@ -18,29 +18,25 @@ import types from unittest import mock +import pytest + def test_docs_conf_executes_successfully(): docs_dir = pathlib.Path(__file__).parent.parent.parent / "docs" conf_path = docs_dir / "conf.py" - if not conf_path.exists(): - import pytest - pytest.skip("docs/conf.py not found") res = runpy.run_path(str(conf_path)) - assert "project" in res - assert res["project"] == "google-cloud-bigquery" + assert res.get("project") == "google-cloud-bigquery" -def test_docs_conf_patches_markdown_translator(): +@pytest.fixture +def fake_translator_and_mock(): docs_dir = pathlib.Path(__file__).parent.parent.parent / "docs" conf_path = docs_dir / "conf.py" - if not conf_path.exists(): - import pytest - pytest.skip("docs/conf.py not found") mock_orig_depart = mock.MagicMock(return_value="original_output") @@ -61,31 +57,41 @@ class FakeTranslator: ): runpy.run_path(str(conf_path)) - assert FakeTranslator.depart_paragraph is not mock_orig_depart - assert FakeTranslator.depart_compact_paragraph is not mock_orig_depart + return FakeTranslator, mock_orig_depart - translator_in_table = FakeTranslator() - translator_in_table.table_entries = ["entry"] - assert ( - FakeTranslator.depart_paragraph(translator_in_table, mock.MagicMock()) is None - ) - assert ( - FakeTranslator.depart_compact_paragraph(translator_in_table, mock.MagicMock()) - is None - ) + +def test_depart_paragraph_suppresses_newlines_inside_table_cells( + fake_translator_and_mock, +): + FakeTranslator, mock_orig_depart = fake_translator_and_mock + translator = FakeTranslator() + translator.table_entries = ["cell"] + node = mock.MagicMock() + + res_paragraph = FakeTranslator.depart_paragraph(translator, node) + res_compact = FakeTranslator.depart_compact_paragraph(translator, node) + + assert res_paragraph is None + assert res_compact is None mock_orig_depart.assert_not_called() - translator_outside_table = FakeTranslator() - mock_node = mock.MagicMock() - assert ( - FakeTranslator.depart_paragraph(translator_outside_table, mock_node) - == "original_output" - ) - mock_orig_depart.assert_called_once_with(translator_outside_table, mock_node) - mock_orig_depart.reset_mock() - assert ( - FakeTranslator.depart_compact_paragraph(translator_outside_table, mock_node) - == "original_output" +def test_depart_paragraph_delegates_outside_table_cells( + fake_translator_and_mock, +): + FakeTranslator, mock_orig_depart = fake_translator_and_mock + translator = FakeTranslator() + node = mock.MagicMock() + + res_paragraph = FakeTranslator.depart_paragraph(translator, node) + res_compact = FakeTranslator.depart_compact_paragraph(translator, node) + + assert res_paragraph == "original_output" + assert res_compact == "original_output" + assert mock_orig_depart.call_count == 2 + mock_orig_depart.assert_has_calls( + [ + mock.call(translator, node), + mock.call(translator, node), + ] ) - mock_orig_depart.assert_called_once_with(translator_outside_table, mock_node)