This is an automated email from the ASF dual-hosted git repository.

dianfu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 818fda57643 [FLINK-41023][python][docs] Build English and Chinese docs 
from one lockfile (#29518)
818fda57643 is described below

commit 818fda576434d41bd469bd7facf900b2271dfff0
Author: Martijn Visser <[email protected]>
AuthorDate: Mon Oct 19 13:58:07 2026 +0200

    [FLINK-41023][python][docs] Build English and Chinese docs from one 
lockfile (#29518)
    
    - Sync a docs venv from docs/uv.lock on Python 3.13 in lint-python.sh and
      build English and Chinese with -W, so PR CI covers both
    - Remove the redundant sphinx dependency group from 
flink-python/pyproject.toml,
      now that docs dependencies are installed from docs/uv.lock
    - Build the Chinese docs with -j auto, like the English ones
    - Fix the language switcher in the mobile sidebar, which kept linking to #,
      and stop it from also cycling the light/dark mode
    - Wrap long page titles instead of scrolling the article sideways
    - Remove the obsolete jQuery-dependent pyflink.js and its broken script 
include
    - Remove CSS rules and switcher.json, with its RAT exclude, that nothing
      uses anymore
    
    Generated-by: Claude Code (Claude Opus 5.5)
---
 .github/workflows/docs.sh                       |  12 +--
 .gitignore                                      |   2 +
 flink-python/dev/lint-python.sh                 |  36 +++-----
 flink-python/docs/Makefile                      |   4 +-
 flink-python/docs/README.md                     |   8 +-
 flink-python/docs/_static/pyflink.css           |  13 +--
 flink-python/docs/_static/pyflink.js            | 116 ------------------------
 flink-python/docs/_static/switcher.json         |  17 ----
 flink-python/docs/_templates/lang-switcher.html |   7 +-
 flink-python/docs/_templates/layout.html        |   1 -
 flink-python/docs/conf.py                       |  10 ++
 flink-python/pyproject.toml                     |   7 --
 pom.xml                                         |   1 -
 13 files changed, 44 insertions(+), 190 deletions(-)

diff --git a/.github/workflows/docs.sh b/.github/workflows/docs.sh
index 185dbfbb287..44039c39975 100755
--- a/.github/workflows/docs.sh
+++ b/.github/workflows/docs.sh
@@ -92,16 +92,8 @@ if [ -f  ./flink-python/dev/lint-python.sh ]; then
     # Set base URL for cross-references to main Flink docs (used by sphinx 
extlinks)
     export 
FLINK_DOCS_BASE_URL="https://nightlies.apache.org/flink/flink-docs-${BRANCH}";
 
-    # build English python docs
-    # disable the gateway, because otherwise it tries to find FLINK_HOME to 
access Java classes
-    PYFLINK_GATEWAY_DISABLED=1 ./flink-python/dev/lint-python.sh -i "sphinx"
-
-    # build Chinese python docs into _build/html/zh/ subdirectory
-    # Ensure uv (installed by lint-python.sh into .uv/bin/) is on PATH so the 
Makefile auto-detects it
-    export PATH="$(pwd)/flink-python/dev/.uv/bin:$PATH"
-    pushd flink-python/docs
-    make zh
-    popd
+    # build English python docs, and Chinese into the zh/ subdirectory
+    ./flink-python/dev/lint-python.sh -i "sphinx"
 
     # move python docs (English at root, Chinese at zh/)
     mv flink-python/docs/_build/html docs/target/api/python
diff --git a/.gitignore b/.gitignore
index 778b0663851..c870d7a4137 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,7 @@ flink-python/pyflink.egg-info/
 flink-python/apache_flink.egg-info/
 flink-python/apache-flink-libraries/apache_flink_libraries.egg-info/
 flink-python/docs/_build
+flink-python/docs/.venv
 flink-python/docs/locales/**/*.mo
 flink-python/docs/locales/**/LC_MESSAGES/*.po
 flink-python/docs/locales/**/reference/
@@ -52,6 +53,7 @@ flink-python/docs/locales/**/examples/
 flink-python/docs/reference/pyflink.*/api/
 flink-python/.tox/
 flink-python/dev/download
+flink-python/dev/.uv/
 flink-python/dev/.conda/
 flink-python/dev/log/
 flink-python/dev/.stage.txt
diff --git a/flink-python/dev/lint-python.sh b/flink-python/dev/lint-python.sh
index 52ecf914cb9..5b1cf8e8429 100755
--- a/flink-python/dev/lint-python.sh
+++ b/flink-python/dev/lint-python.sh
@@ -302,30 +302,16 @@ function install_flake8() {
 }
 
 # Install sphinx.
-# In some situations,you need to run the script with "sudo". e.g. sudo 
./lint-python.sh
+# The docs toolchain is pinned by docs/uv.lock. It gets its own venv outside 
.uv/envs,
+# because activate() puts every env in there on PATH for tox.
 function install_sphinx() {
-    source $UV_HOME/bin/activate
-    if [ -f "$SPHINX_PATH" ]; then
-        $UV_PATH pip uninstall Sphinx -q 2>&1 >/dev/null
-        if [ $? -ne 0 ]; then
-            echo "uv pip uninstall sphinx failed \
-            please try to exec the script again.\
-            if failed many times, you can try to exec in the form of sudo 
./lint-python.sh -f"
-            exit 1
-        fi
-    fi
-
-    $CURRENT_DIR/install_command.sh -q --group "${PYPROJECT_PATH}:sphinx" 2>&1 
>/dev/null
+    UV_PYTHON=3.13 UV_PROJECT_ENVIRONMENT="$DOCS_ENV" $UV_PATH sync -q 
--locked --project "$FLINK_PYTHON_DIR/docs"
     if [ $? -ne 0 ]; then
-        echo "uv pip install sphinx failed \
-        please try to exec the script again.\
-        if failed many times, you can try to exec in the form of sudo 
./lint-python.sh -f"
+        echo "uv sync of the docs environment failed; if 
flink-python/docs/pyproject.toml changed, run uv lock in flink-python/docs."
         exit 1
     fi
-    deactivate
 }
 
-
 # Install mypy.
 # In some situations, you need to run the script with "sudo". e.g. sudo 
./lint-python.sh
 function install_mypy() {
@@ -630,7 +616,8 @@ function flake8_check() {
 
 # Sphinx check
 function sphinx_check() {
-    export SPHINXBUILD=$SPHINX_PATH
+    # sync again: the install step is skipped when .stage.txt says it already 
ran
+    install_sphinx
     # cd to $FLINK_PYTHON_DIR
     pushd "$FLINK_PYTHON_DIR"/docs &> /dev/null
     make clean
@@ -638,7 +625,10 @@ function sphinx_check() {
     # the return value of a pipeline is the status of the last command to exit
     # with a non-zero status or zero if no command exited with a non-zero 
status
     set -o pipefail
-    (SPHINXOPTS="-a -W" make html) 2>&1 | tee -a $LOG_FILE
+    # disable the gateway, because otherwise it tries to find FLINK_HOME to 
access Java classes.
+    # Not exported: the tox check runs after this one and needs the gateway.
+    (PYFLINK_GATEWAY_DISABLED=1 SPHINXBUILD="$SPHINX_PATH" 
SPHINXINTL="$DOCS_ENV/bin/sphinx-intl" \
+        SPHINXOPTS="-a -W" make html-zh) 2>&1 | tee -a $LOG_FILE
 
     SPHINXBUILD_STATUS=$?
     if [ $SPHINXBUILD_STATUS -ne 0 ]; then
@@ -700,8 +690,11 @@ TOX_PATH=$ENV_HOME/bin/tox
 # flake8 path
 FLAKE8_PATH=$ENV_HOME/bin/flake8
 
+# docs venv, see install_sphinx
+DOCS_ENV=$CURRENT_DIR/.uv/docs-venv
+
 # sphinx path
-SPHINX_PATH=$ENV_HOME/bin/sphinx-build
+SPHINX_PATH=$DOCS_ENV/bin/sphinx-build
 
 # mypy path
 MYPY_PATH=$ENV_HOME/bin/mypy
@@ -832,6 +825,7 @@ skip_checks=0
 if [[ ${CLEAN_UP_FLAG} -eq 1 ]]; then
     printf "clean up python environment"
     rm -rf ${UV_HOME}
+    rm -rf ${DOCS_ENV}
     rm -rf ${STAGE_FILE}
     rm -rf ${FLINK_PYTHON_DIR}/.tox
     skip_checks=1
diff --git a/flink-python/docs/Makefile b/flink-python/docs/Makefile
index 5f461597602..bf65bda8692 100644
--- a/flink-python/docs/Makefile
+++ b/flink-python/docs/Makefile
@@ -27,6 +27,8 @@ ifneq ($(shell command -v uv 2>/dev/null),)
 SPHINXBUILD   = uv run sphinx-build
 SPHINXINTL    = uv run sphinx-intl
 PYTHON        = uv run python
+# same Python as the docs build in flink-python/dev/lint-python.sh
+export UV_PYTHON ?= 3.13
 else
 SPHINXBUILD   = sphinx-build
 SPHINXINTL    = sphinx-intl
@@ -85,7 +87,7 @@ html-zh: html zh
 
 zh: gettext
        $(SPHINXINTL) update -p _build/gettext -l zh
-       $(SPHINXBUILD) -b html -D language=zh -d _build/doctrees-zh 
$(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . _build/html/zh
+       $(SPHINXBUILD) -b html -D language=zh -j auto -d _build/doctrees-zh 
$(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . _build/html/zh
        @echo
        @echo "Build finished. The Chinese HTML pages are in _build/html/zh."
 
diff --git a/flink-python/docs/README.md b/flink-python/docs/README.md
index 0be77fd3e4b..72a9bf74e74 100644
--- a/flink-python/docs/README.md
+++ b/flink-python/docs/README.md
@@ -33,10 +33,10 @@ The docs directory has its own `pyproject.toml` with all 
Sphinx dependencies. Yo
 
 ```bash
 cd flink-python/docs
-uv sync
+UV_PYTHON=3.13 uv sync
 ```
 
-That's it. The Makefile auto-detects uv and will use `uv run` to invoke Sphinx.
+That's it. The Makefile auto-detects uv and will use `uv run` to invoke 
Sphinx, on Python 3.13 unless `UV_PYTHON` is set.
 
 ### Using pip
 
@@ -67,9 +67,11 @@ make html-zh
 
 This builds English into `_build/html/` and Chinese into `_build/html/zh/`. A 
language toggle in the navbar allows switching between them.
 
+CI runs this build with `-W` from the locked environment through 
`flink-python/dev/lint-python.sh -i sphinx`. Run that to reproduce a CI 
failure. Like `make zh`, it updates the Chinese `.po` files when the English 
text changed, see [Syncing after English content 
changes](#syncing-after-english-content-changes).
+
 ### Chinese only
 
-If the English docs are already built (e.g., in CI where `lint-python.sh` 
builds them first):
+If the English docs are already built:
 
 ```bash
 make zh
diff --git a/flink-python/docs/_static/pyflink.css 
b/flink-python/docs/_static/pyflink.css
index bffebe98d7b..6e2bc7b62c2 100644
--- a/flink-python/docs/_static/pyflink.css
+++ b/flink-python/docs/_static/pyflink.css
@@ -15,16 +15,9 @@
  limitations under the License.
 */
 
-div div.sphinxsidebar {
-    width: 274px;
-}
-
-div div.bodywrapper {
-    margin: 0 0 0 274px;
-}
-
-div div.body {
-    max-width: none;
+/* long API names have no break opportunities and would overflow the article */
+.bd-article h1 {
+    overflow-wrap: anywhere;
 }
 
 dl.confval .sig-name.descname span.pre {
diff --git a/flink-python/docs/_static/pyflink.js 
b/flink-python/docs/_static/pyflink.js
deleted file mode 100644
index 113ae1f9e7d..00000000000
--- a/flink-python/docs/_static/pyflink.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You 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.
-*/
-
-$(function () {
-
-    function startsWith(s, prefix) {
-        return s && s.indexOf(prefix) === 0;
-    }
-
-    // Constructs map stores modules
-    function buildModuleLinkMap() {
-        var moduleLinkMap = {};
-        $('div.sphinxsidebar a.reference.internal').each(function (_, a) {
-            var href = $(a).attr('href');
-            // Whether is a module.
-            if (startsWith(href, '#module-')) {
-                var moduleName = href.substr(8);
-                moduleLinkMap[moduleName] = [$(a), null];
-            }
-        });
-        return moduleLinkMap;
-    }
-
-    function getDivNoteMap(dd) {
-        //Stores the divs that includes 'Special note' such as 
'Deprecated',Experimental.
-        var divNoteMap = {};
-        // <div class="admonition note">
-        //      <p class="first admonition-title">Note</p>
-        //      <p class="last">WARN: Flink Internal Use Only</p>
-        // </div>
-        // The structure will get 'WARN: Flink Internal Use Only'
-        dd.find('> div.admonition.note > p.last').each(function (_, p) {
-            var text = $(p).text();
-            // Whether is Deprecated.
-            if (!divNoteMap.deprecated && startsWith(text, 'Deprecated')) {
-                divNoteMap.deprecated = $(p).parent();
-            }
-            // Whether is Experimental.
-            if (!divNoteMap.experimental && startsWith(text, 'Experimental')) {
-                divNoteMap.experimental = $(p).parent();
-            }
-        });
-        return divNoteMap;
-    }
-
-    function getPackageName(name) {
-        var last_idx = name.lastIndexOf('.');
-        return last_idx === -1 ? '' : name.substr(0, last_idx);
-    }
-
-    function buildTag(text, cls, tooltip) {
-        return '<span class="pys-tag ' + cls + ' hasTooltip">' + text + '<span 
class="tooltip">'
-            + tooltip + '</span></span>'
-    }
-
-
-    var moduleLinkMap = buildModuleLinkMap();
-
-    // The class field of dl label includes 'class','method','classmethod'.
-    $('dl.class, dl.function').each(function (_, dl) {
-
-        dl = $(dl);
-        var dt = dl.children('dt').eq(0);
-        var dd = dl.children('dd').eq(0);
-
-        // Name of the class(method,classmethod).
-        // e.g. <dt id="pyflink.table.TableEnvironment"> will get 
'pyflink.table.TableEnvironment'
-        var name = dt.attr('id');
-        // Desc of the class(method,classmethod).
-        // e.g. <code class="descname">TableEnvironment</code> will get 
'TableEnvironment'
-        var descName = dt.find('> .descname').text();
-        var divNoteMap = getDivNoteMap(dd);
-
-        if (name) {
-            // e.g. 'pyflink.table.TableEnvironment' will get 'pyflink.table'
-            var packageName = getPackageName(name);
-
-            var moduleLink = moduleLinkMap[packageName];
-            if (moduleLink) {
-                if (moduleLink[1] === null) {
-                    moduleLink[1] = $('<ul/>');
-                    moduleLink[0].parent().append(moduleLink[1]);
-                }
-                var tags = '';
-                if (divNoteMap.experimental) {
-                    tags += buildTag('E', 'pys-tag-experimental', 
'Experimental');
-                    divNoteMap.experimental.addClass('pys-note 
pys-note-experimental');
-                }
-                if (divNoteMap.deprecated) {
-                    tags += buildTag('D', 'pys-tag-deprecated', 'Deprecated');
-                    divNoteMap.deprecated.addClass('pys-note 
pys-note-deprecated');
-                }
-                var li = $('<li/>');
-                var a = $('<a href="#' + name + '">' + descName + '</a>');
-                li.append(a);
-                li.append(tags);
-                moduleLink[1].append(li);
-                moduleLinkMap[name] = [a, null];
-            }
-        }
-    });
-});
diff --git a/flink-python/docs/_static/switcher.json 
b/flink-python/docs/_static/switcher.json
deleted file mode 100644
index 3a57b584c11..00000000000
--- a/flink-python/docs/_static/switcher.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
-  {
-    "name": "dev",
-    "version": "master",
-    "url": "https://nightlies.apache.org/flink/flink-docs-master/api/python";
-  },
-  {
-    "name": "1.16 (stable)",
-    "version": "release-1.16",
-    "url": 
"https://nightlies.apache.org/flink/flink-docs-release-1.16/api/python/";
-  },
-  {
-    "name": "1.15",
-    "version": "release-1.15",
-    "url": 
"https://nightlies.apache.org/flink/flink-docs-release-1.15/api/python/";
-  }
-]
diff --git a/flink-python/docs/_templates/lang-switcher.html 
b/flink-python/docs/_templates/lang-switcher.html
index b878c4105ce..dc347e41331 100644
--- a/flink-python/docs/_templates/lang-switcher.html
+++ b/flink-python/docs/_templates/lang-switcher.html
@@ -17,15 +17,16 @@ specific language governing permissions and limitations
 under the License.
 -->
 {# Language switcher: links to the same page in the other language #}
-<span class="theme-switch-button btn btn-sm btn-outline-primary navbar-btn 
rounded-circle" data-toggle="tooltip"
+<span class="btn btn-sm btn-outline-primary navbar-btn rounded-circle" 
data-toggle="tooltip"
   {% if language == 'zh' %}title="Switch to English"{% else %}title="切换到中文"{% 
endif %}>
-  <a id="lang-switch" href="#" style="display:flex">
+  <a href="#" style="display:flex">
     <i class="fa-solid fa-language"></i>
   </a>
 </span>
 <script>
 (function() {
-  var link = document.getElementById('lang-switch');
+  // The theme renders the navbar twice (header and mobile sidebar), so find 
this copy's link
+  var link = document.currentScript.previousElementSibling.querySelector('a');
   var path = window.location.pathname;
   {% if language == 'zh' %}
   // Chinese -> English: remove the /zh/ path segment
diff --git a/flink-python/docs/_templates/layout.html 
b/flink-python/docs/_templates/layout.html
index 0f4073931e1..d8b40ffd3f7 100644
--- a/flink-python/docs/_templates/layout.html
+++ b/flink-python/docs/_templates/layout.html
@@ -19,7 +19,6 @@ under the License.
 {% extends "!layout.html" %}
 {% block linktags %}
 {{ super() }}
-<script src="_static/pyflink.js" ></script>
 <!-- Matomo -->
 <script>
   var _paq = window._paq = window._paq || [];
diff --git a/flink-python/docs/conf.py b/flink-python/docs/conf.py
index 77e8dd08136..4ca29b9d40c 100644
--- a/flink-python/docs/conf.py
+++ b/flink-python/docs/conf.py
@@ -246,3 +246,13 @@ epub_copyright = u'2019, Author'
 epub_exclude_files = ['search.html']
 
 # -- Extension configuration -------------------------------------------------
+
+
+def _set_zh_baseurl(app, config):
+    # -D language=zh is applied after this file is read
+    if config.language == 'zh':
+        config.html_baseurl += 'zh/'
+
+
+def setup(app):
+    app.connect('config-inited', _set_zh_baseurl)
diff --git a/flink-python/pyproject.toml b/flink-python/pyproject.toml
index 83fbe240f97..e8cd3815419 100644
--- a/flink-python/pyproject.toml
+++ b/flink-python/pyproject.toml
@@ -60,13 +60,6 @@ tox = [
 flake8 = [
     "flake8==7.2.0"
 ]
-sphinx = [
-    "Sphinx==7.4.7",
-    "Docutils==0.21.2",
-    "pydata_sphinx_theme==0.16.1",
-    "sphinx_mdinclude==0.6.2",
-    "sphinx-intl==2.3.2"
-]
 mypy = [
     "mypy==1.5.1",
     "types-pytz",
diff --git a/pom.xml b/pom.xml
index 8e30270db65..25dedde3609 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1742,7 +1742,6 @@ under the License.
                                                
<inputExclude>flink-python/lib/**</inputExclude>
                                                
<inputExclude>flink-python/dev/download/**</inputExclude>
                                                
<inputExclude>flink-python/docs/_build/**</inputExclude>
-                                               
<inputExclude>flink-python/docs/_static/switcher.json</inputExclude>
                                                
<inputExclude>flink-python/docs/uv.lock</inputExclude>
 
                                                <!-- AWS SDK config that does 
not support license headers -->

Reply via email to