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 -->