diff --git a/devel-common/src/sphinx_exts/docs_build/docs_builder.py b/devel-common/src/sphinx_exts/docs_build/docs_builder.py
index 147215a7353b2..12bf36e79a4fe 100644
--- a/devel-common/src/sphinx_exts/docs_build/docs_builder.py
+++ b/devel-common/src/sphinx_exts/docs_build/docs_builder.py
@@ -124,6 +124,8 @@ def _src_dir(self) -> Path:
return (AIRFLOW_CONTENT_ROOT_PATH / "providers").joinpath(*package_paths) / "docs"
if self.package_name == "apache-airflow-ctl":
return AIRFLOW_CONTENT_ROOT_PATH / "airflow-ctl" / "docs"
+ if self.package_name == "apache-airflow-mypy":
+ return AIRFLOW_CONTENT_ROOT_PATH / "dev" / "mypy" / "docs"
if self.package_name == "task-sdk":
return AIRFLOW_CONTENT_ROOT_PATH / "task-sdk" / "docs"
console.print(f"[red]Unknown package name: {self.package_name}")
@@ -342,6 +344,7 @@ def get_available_packages(include_suspended: bool = False, short_form: bool = F
*provider_names,
"apache-airflow-providers",
"apache-airflow-ctl",
+ "apache-airflow-mypy",
"task-sdk",
"helm-chart",
"docker-stack",
diff --git a/devel-common/tests/unit/sphinx_exts/docs_build/test_docs_builder.py b/devel-common/tests/unit/sphinx_exts/docs_build/test_docs_builder.py
new file mode 100644
index 0000000000000..49434230c10b4
--- /dev/null
+++ b/devel-common/tests/unit/sphinx_exts/docs_build/test_docs_builder.py
@@ -0,0 +1,30 @@
+# 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.
+from __future__ import annotations
+
+from sphinx_exts.docs_build.code_utils import AIRFLOW_CONTENT_ROOT_PATH
+from sphinx_exts.docs_build.docs_builder import AirflowDocsBuilder, get_available_packages
+
+
+def test_mypy_docs_package_is_available():
+ assert "apache-airflow-mypy" in get_available_packages()
+
+
+def test_mypy_docs_source_directory():
+ builder = AirflowDocsBuilder(package_name="apache-airflow-mypy")
+
+ assert builder._src_dir == AIRFLOW_CONTENT_ROOT_PATH / "dev" / "mypy" / "docs"
diff --git a/docs/README.md b/docs/README.md
index 28ae32a1db8e0..02c4af4b5d85a 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -50,6 +50,7 @@ Documentation in separate distributions:
* `chart/docs` - documentation for the Helm Chart
* `task-sdk/docs` - documentation for Task SDK (new format not yet published)
* `airflow-ctl/docs` - documentation for Airflow CLI
+* `dev/mypy/docs` - documentation for the Apache Airflow Mypy plugins
Documentation for a general overview and summaries not connected with any specific distribution:
@@ -141,7 +142,7 @@ You can also specify whether documentation should be published to `live` or `sta
The person who triggers the build (release manager) should specify the tag name of the docs to be published
and the list of documentation packages to be published. Usually it is:
-* Airflow: `apache-airflow docker-stack task-sdk apache-airflow-ctl`
+* Airflow: `apache-airflow docker-stack task-sdk apache-airflow-ctl apache-airflow-mypy`
* Helm chart: `helm-chart`
* Providers: `provider_id1 provider_id2` or `all providers` if all providers should be published.
diff --git a/scripts/ci/docs/store_stable_versions.py b/scripts/ci/docs/store_stable_versions.py
index 91fb8247761b4..c00d7ab79f8f5 100755
--- a/scripts/ci/docs/store_stable_versions.py
+++ b/scripts/ci/docs/store_stable_versions.py
@@ -105,6 +105,11 @@ def get_package_version(package_name: str, airflow_root: Path) -> str | None:
if package_name == "apache-airflow-ctl":
return get_version_from_init_py(airflow_root / "airflow-ctl" / "src" / "airflowctl" / "__init__.py")
+ if package_name == "apache-airflow-mypy":
+ return get_version_from_init_py(
+ airflow_root / "dev" / "mypy" / "src" / "airflow_mypy" / "__init__.py"
+ )
+
if package_name == "task-sdk":
return get_version_from_init_py(airflow_root / "task-sdk" / "src" / "airflow" / "sdk" / "__init__.py")
diff --git a/scripts/ci/prek/mypy_folder.py b/scripts/ci/prek/mypy_folder.py
index 420017c161616..860755ff6894e 100755
--- a/scripts/ci/prek/mypy_folder.py
+++ b/scripts/ci/prek/mypy_folder.py
@@ -45,6 +45,7 @@
"airflow-core",
*[f"providers/{provider_id.replace('.', '/')}" for provider_id in get_all_provider_ids()],
"dev",
+ "dev/mypy",
"scripts",
"devel-common",
"task-sdk",
diff --git a/scripts/tests/ci/docs/test_store_stable_versions.py b/scripts/tests/ci/docs/test_store_stable_versions.py
index ef02e2e5787d2..9755256dbbc1f 100644
--- a/scripts/tests/ci/docs/test_store_stable_versions.py
+++ b/scripts/tests/ci/docs/test_store_stable_versions.py
@@ -155,6 +155,13 @@ def test_apache_airflow_ctl(self, tmp_path):
assert get_package_version("apache-airflow-ctl", tmp_path) == "0.1.3"
+ def test_apache_airflow_mypy(self, tmp_path):
+ init_file = tmp_path / "dev" / "mypy" / "src" / "airflow_mypy" / "__init__.py"
+ init_file.parent.mkdir(parents=True)
+ init_file.write_text('__version__ = "0.2.0"\n')
+
+ assert get_package_version("apache-airflow-mypy", tmp_path) == "0.2.0"
+
def test_task_sdk(self, tmp_path):
init_file = tmp_path / "task-sdk" / "src" / "airflow" / "sdk" / "__init__.py"
init_file.parent.mkdir(parents=True)
@@ -212,6 +219,9 @@ def _create_fake_airflow_root(self, tmp_path):
ctl_init = airflow_root / "airflow-ctl" / "src" / "airflowctl" / "__init__.py"
ctl_init.parent.mkdir(parents=True)
ctl_init.write_text('__version__ = "0.1.3"\n')
+ mypy_init = airflow_root / "dev" / "mypy" / "src" / "airflow_mypy" / "__init__.py"
+ mypy_init.parent.mkdir(parents=True)
+ mypy_init.write_text('__version__ = "0.2.0"\n')
return airflow_root
def _create_docs_build_dir(self, tmp_path, packages):
@@ -225,7 +235,9 @@ def _create_docs_build_dir(self, tmp_path, packages):
def test_creates_stable_txt(self, tmp_path, monkeypatch):
airflow_root = self._create_fake_airflow_root(tmp_path)
- docs_dir = self._create_docs_build_dir(tmp_path, ["apache-airflow", "apache-airflow-ctl"])
+ docs_dir = self._create_docs_build_dir(
+ tmp_path, ["apache-airflow", "apache-airflow-ctl", "apache-airflow-mypy"]
+ )
monkeypatch.setenv("DOCS_BUILD_DIR", str(docs_dir))
monkeypatch.setenv("AIRFLOW_ROOT", str(airflow_root))
@@ -235,6 +247,7 @@ def test_creates_stable_txt(self, tmp_path, monkeypatch):
assert result == 0
assert (docs_dir / "apache-airflow" / "stable.txt").read_text() == "3.2.0\n"
assert (docs_dir / "apache-airflow-ctl" / "stable.txt").read_text() == "0.1.3\n"
+ assert (docs_dir / "apache-airflow-mypy" / "stable.txt").read_text() == "0.2.0\n"
def test_creates_versioned_directory(self, tmp_path, monkeypatch):
airflow_root = self._create_fake_airflow_root(tmp_path)
diff --git a/uv.lock b/uv.lock
index ef8a6032697c5..fd05489f13a90 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2513,6 +2513,7 @@ docs = [
{ name = "sphinxcontrib-serializinghtml" },
{ name = "sphinxcontrib-spelling" },
{ name = "swagger-plugin-for-sphinx" },
+ { name = "tabulate" },
]
docs-gen = [
{ name = "diagrams" },
@@ -2702,6 +2703,7 @@ requires-dist = [
{ name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'sqlalchemy'", specifier = ">=1.4.49" },
{ name = "sqlalchemy-utils", marker = "extra == 'sqlalchemy'", specifier = ">=0.41.2" },
{ name = "swagger-plugin-for-sphinx", marker = "extra == 'docs'", specifier = ">=7.1.0,!=7.2.0" },
+ { name = "tabulate", marker = "extra == 'docs'", specifier = ">=0.9.0" },
{ name = "time-machine", extras = ["dateutil"], specifier = ">=3.0.0" },
{ name = "towncrier", marker = "extra == 'devscripts'", specifier = ">=23.11.0" },
{ name = "twine", marker = "extra == 'devscripts'", specifier = ">=4.0.2" },
@@ -2876,9 +2878,21 @@ dependencies = [
{ name = "mypy" },
]
+[package.dev-dependencies]
+docs = [
+ { name = "apache-airflow-core" },
+ { name = "apache-airflow-devel-common", extra = ["docs"] },
+]
+
[package.metadata]
requires-dist = [{ name = "mypy", specifier = ">=1.0.0" }]
+[package.metadata.requires-dev]
+docs = [
+ { name = "apache-airflow-core", editable = "airflow-core" },
+ { name = "apache-airflow-devel-common", extras = ["docs"], editable = "devel-common" },
+]
+
[[package]]
name = "apache-airflow-providers"
version = "0.0.1"