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

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 8f96fbd6f67 Auto-sync provider README.rst Requirements with 
pyproject.toml (#67669)
8f96fbd6f67 is described below

commit 8f96fbd6f67f54dbb1dbfb109680bd68b6cd0306
Author: Jarek Potiuk <[email protected]>
AuthorDate: Fri May 29 20:15:04 2026 +0200

    Auto-sync provider README.rst Requirements with pyproject.toml (#67669)
    
    Provider README.rst files are generated from pyproject.toml at
    distribution-build time. Between releases, dependency bumps in
    pyproject.toml do not propagate to the committed README, leading to
    silent drift that has to be fixed manually (#67554).
    
    Add a prek hook that re-renders the Requirements table from the current
    pyproject.toml whenever the latter changes, catching drift in pre-commit
    and CI instead of waiting for a release build. Only the table block is
    touched — surrounding prose, the cross-provider table, and the rest of
    the README are left alone.
    
    Also brings 7 currently-stale provider READMEs into sync as the hook
    output (apache/beam, apache/spark, fab, ibm/mq, microsoft/azure, mysql,
    tableau) — these are the same class of drift #67554 fixed manually for
    celery, cncf/kubernetes, edge3, git, and openlineage.
---
 .pre-commit-config.yaml                 |  11 +++
 providers/apache/beam/README.rst        |   6 +-
 providers/apache/spark/README.rst       |   2 +
 providers/fab/README.rst                |   2 +-
 providers/ibm/mq/README.rst             |  15 +--
 providers/microsoft/azure/README.rst    |   8 +-
 providers/mysql/README.rst              |   1 +
 providers/tableau/README.rst            |   2 +-
 scripts/ci/prek/sync_provider_readme.py | 158 ++++++++++++++++++++++++++++++++
 9 files changed, 190 insertions(+), 15 deletions(-)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ad15f0b15d6..c084a48f152 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -938,6 +938,17 @@ repos:
         language: python
         files: ^README\.md$
         pass_filenames: false
+      - id: sync-provider-readme
+        name: Sync provider README.rst Requirements table with pyproject.toml
+        description: |
+          Provider README.rst files are regenerated from pyproject.toml at 
distribution-build
+          time. Between releases, dependency bumps in pyproject.toml do not 
propagate to the
+          committed README, leading to drift. This hook re-renders the 
Requirements table from
+          the current pyproject.toml whenever the latter changes.
+        entry: ./scripts/ci/prek/sync_provider_readme.py
+        language: python
+        files: ^providers/.*/pyproject\.toml$
+        pass_filenames: true
       - id: lint-markdown
         name: Run markdownlint
         description: Checks the style of Markdown files.
diff --git a/providers/apache/beam/README.rst b/providers/apache/beam/README.rst
index 9b8c9c54256..e152d0223ed 100644
--- a/providers/apache/beam/README.rst
+++ b/providers/apache/beam/README.rst
@@ -56,10 +56,12 @@ PIP package                                 Version required
 ``apache-airflow``                          ``>=2.11.0``
 ``apache-airflow-providers-common-compat``  ``>=1.12.0``
 ``apache-beam``                             ``>=2.69.0``
-``pyarrow``                                 ``>=16.1.0``
+``pyarrow``                                 ``>=16.1.0; python_version < 
"3.14"``
+``pyarrow``                                 ``>=22.0.0; python_version >= 
"3.14"``
 ``numpy``                                   ``>=1.22.4; python_version < 
"3.11"``
 ``numpy``                                   ``>=1.23.2; python_version < 
"3.12" and python_version >= "3.11"``
-``numpy``                                   ``>=1.26.0; python_version >= 
"3.12"``
+``numpy``                                   ``>=1.26.0; python_version >= 
"3.12" and python_version < "3.14"``
+``numpy``                                   ``>=2.4.3; python_version >= 
"3.14"``
 ==========================================  
==================================================================
 
 Cross provider package dependencies
diff --git a/providers/apache/spark/README.rst 
b/providers/apache/spark/README.rst
index 1fdc944ef96..8ffc567a161 100644
--- a/providers/apache/spark/README.rst
+++ b/providers/apache/spark/README.rst
@@ -57,6 +57,8 @@ PIP package                                 Version required
 ``apache-airflow-providers-common-compat``  ``>=1.12.0``
 ``pyspark-client``                          ``>=4.0.0``
 ``grpcio-status``                           ``>=1.67.0``
+``requests``                                ``>=2.32.0``
+``tenacity``                                ``>=8.3.0``
 ==========================================  ==================
 
 Cross provider package dependencies
diff --git a/providers/fab/README.rst b/providers/fab/README.rst
index d4b350483a5..0856d28853b 100644
--- a/providers/fab/README.rst
+++ b/providers/fab/README.rst
@@ -72,7 +72,7 @@ PIP package                                 Version required
 ``wtforms``                                 ``>=3.0``
 ``cachetools``                              ``>=6.0``
 ``marshmallow``                             ``>=3``
-``flask_limiter``                           ``>3,!=3.13``
+``flask_limiter``                           ``>3``
 ==========================================  
=====================================
 
 Cross provider package dependencies
diff --git a/providers/ibm/mq/README.rst b/providers/ibm/mq/README.rst
index f08c8d1bc0d..59efe30eefa 100644
--- a/providers/ibm/mq/README.rst
+++ b/providers/ibm/mq/README.rst
@@ -63,13 +63,14 @@ The package supports the following python versions: 
3.10,3.11,3.12,3.13
 Requirements
 ------------
 
-=============================================  
=====================================
-PIP package                                    Version required
-=============================================  
=====================================
-``apache-airflow``                             ``>=2.11.0``
-``apache-airflow-providers-common-messaging``  ``>=2.0.0``
-``importlib-resources``                        ``>=1.3``
-=============================================  
=====================================
+==========================================  
======================================
+PIP package                                 Version required
+==========================================  
======================================
+``apache-airflow``                          ``>=2.11.0``
+``apache-airflow-providers-common-compat``  ``>=1.12.0``
+``asgiref``                                 ``>=2.3.0; python_version < 
"3.14"``
+``asgiref``                                 ``>=3.11.1; python_version >= 
"3.14"``
+==========================================  
======================================
 
 
 
========================================================================================================================
  ====================
diff --git a/providers/microsoft/azure/README.rst 
b/providers/microsoft/azure/README.rst
index b22d4fa519e..fa58eff6bbc 100644
--- a/providers/microsoft/azure/README.rst
+++ b/providers/microsoft/azure/README.rst
@@ -50,9 +50,9 @@ The package supports the following python versions: 
3.10,3.11,3.12,3.13,3.14
 Requirements
 ------------
 
-==========================================  ===========================
+==========================================  ===================
 PIP package                                 Version required
-==========================================  ===========================
+==========================================  ===================
 ``apache-airflow``                          ``>=2.11.0``
 ``apache-airflow-providers-common-compat``  ``>=1.13.0``
 ``adlfs``                                   ``>=2023.10.0``
@@ -71,7 +71,7 @@ PIP package                                 Version required
 ``azure-synapse-spark``                     ``>=0.2.0``
 ``azure-synapse-artifacts``                 ``>=0.17.0``
 ``azure-storage-file-datalake``             ``>=12.9.1``
-``azure-kusto-data``                        ``>=4.1.0,!=4.6.0,!=5.0.0``
+``azure-kusto-data``                        ``>=4.1.0,!=5.0.0``
 ``azure-mgmt-datafactory``                  ``>=2.0.0``
 ``azure-mgmt-containerregistry``            ``>=8.0.0``
 ``azure-mgmt-compute``                      ``>=33.0.0``
@@ -84,7 +84,7 @@ PIP package                                 Version required
 ``microsoft-kiota-abstractions``            ``>=1.9.4,<2.0.0``
 ``microsoft-kiota-authentication-azure``    ``>=1.9.4,<2.0.0``
 ``msal-extensions``                         ``>=1.3.0``
-==========================================  ===========================
+==========================================  ===================
 
 Cross provider package dependencies
 -----------------------------------
diff --git a/providers/mysql/README.rst b/providers/mysql/README.rst
index ef562d7777e..87f9aa43327 100644
--- a/providers/mysql/README.rst
+++ b/providers/mysql/README.rst
@@ -60,6 +60,7 @@ PIP package                                 Version required
 ``mysql-connector-python``                  ``>=9.1.0; python_version < 
"3.12"``
 ``mysql-connector-python``                  ``>=9.1.0,!=9.7.0; python_version 
>= "3.12"``
 ``aiomysql``                                ``>=0.2.0``
+``pymysql``                                 ``>=1.0.3,<1.2``
 ==========================================  
=============================================
 
 Cross provider package dependencies
diff --git a/providers/tableau/README.rst b/providers/tableau/README.rst
index 73100612e48..1c1f55adbca 100644
--- a/providers/tableau/README.rst
+++ b/providers/tableau/README.rst
@@ -55,7 +55,7 @@ PIP package                                 Version required
 ==========================================  ==================
 ``apache-airflow``                          ``>=2.11.0``
 ``apache-airflow-providers-common-compat``  ``>=1.10.1``
-``tableauserverclient``                     ``>=0.27,!=0.39``
+``tableauserverclient``                     ``>=0.27``
 ==========================================  ==================
 
 Cross provider package dependencies
diff --git a/scripts/ci/prek/sync_provider_readme.py 
b/scripts/ci/prek/sync_provider_readme.py
new file mode 100755
index 00000000000..efe72981518
--- /dev/null
+++ b/scripts/ci/prek/sync_provider_readme.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+# 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.
+# /// script
+# requires-python = ">=3.10"
+# dependencies = [
+#   "packaging>=25",
+#   "tabulate>=0.9.0",
+#   "tomli>=2.0.1; python_version < '3.11'",
+# ]
+# ///
+"""
+Sync the ``Requirements`` table in each provider ``README.rst`` with the
+``[project].dependencies`` block of its sibling ``pyproject.toml``.
+
+Provider README files are generated at distribution-build time by
+``dev/breeze/src/airflow_breeze/utils/packages.py``'s ``_prepare_readme_file``.
+Between releases they are committed in-repo as the rendered output. A
+dependency bump in ``pyproject.toml`` does not propagate to the committed
+README until the next release build, leading to drift like the one fixed
+manually in https://github.com/apache/airflow/pull/67554.
+
+This hook re-renders the ``Requirements`` table directly from the current
+``pyproject.toml`` whenever the latter changes, using the same column shape
+``convert_pip_requirements_to_table`` produces. Only the table block is
+touched — surrounding prose, the cross-provider table, and the rest of the
+README are left alone.
+
+Run as a prek hook with the changed ``providers/*/pyproject.toml`` paths as
+positional arguments.
+"""
+
+from __future__ import annotations
+
+import re
+import sys
+from pathlib import Path
+
+try:
+    import tomllib
+except ModuleNotFoundError:  # py < 3.11 fallback (CI runs ≥3.10)
+    import tomli as tomllib  # type: ignore[no-redef]
+
+from packaging.requirements import Requirement
+from tabulate import tabulate
+
+HEADERS = ["PIP package", "Version required"]
+
+# Matches the "Requirements" section + its following RST grid table:
+#
+#     Requirements
+#     ------------
+#
+#     ============  ==============
+#     PIP package   Version required
+#     ============  ==============
+#     ``foo``       ``>=1.0``
+#     ============  ==============
+#
+# The pattern captures the heading + leading blank line as group(1) so the
+# substitution can re-emit it verbatim, then matches the full table block —
+# three ``===`` separator lines bracketing the header row and the data rows.
+# The MULTILINE+DOTALL flags let the body span multiple lines while keeping
+# the boundary anchors tight.
+TABLE_PATTERN = re.compile(
+    r"(?ms)"
+    r"(^Requirements\s*\n-+\s*\n\s*\n)"  # group(1): heading + blank
+    r"=+ +=+[^\S\n]*\n"  # opening ===
+    r".+?\n"  # header row
+    r"=+ +=+[^\S\n]*\n"  # === after header
+    r".+?\n"  # data rows (non-greedy)
+    r"=+ +=+[^\S\n]*\n"  # closing ===
+)
+
+
+def render_requirements_table(dependencies: list[str]) -> str:
+    """Render the same RST table shape that breeze's release-builder 
produces."""
+    rows: list[tuple[str, str]] = []
+    for dep in dependencies:
+        req = Requirement(dep)
+        package = req.name
+        if req.extras:
+            package += f"[{','.join(sorted(req.extras))}]"
+        version_required = ""
+        if req.specifier:
+            version_required = ",".join(
+                str(spec) for spec in sorted(req.specifier, key=lambda spec: 
spec.version)
+            )
+        if req.marker:
+            version_required += f"; {req.marker}"
+        version_required = version_required.strip()
+        formatted_version = f"``{version_required}``" if version_required else 
""
+        rows.append((f"``{package}``", formatted_version))
+    return tabulate(rows, headers=HEADERS, tablefmt="rst")
+
+
+def sync_one(pyproject_path: Path) -> bool:
+    """Sync the README.rst next to ``pyproject_path``. Returns True on 
change."""
+    readme_path = pyproject_path.parent / "README.rst"
+    if not readme_path.exists():
+        return False
+    try:
+        data = tomllib.loads(pyproject_path.read_text())
+    except tomllib.TOMLDecodeError as exc:
+        print(f"[sync-provider-readme] could not parse {pyproject_path}: 
{exc}", file=sys.stderr)
+        return False
+    dependencies = data.get("project", {}).get("dependencies") or []
+    if not dependencies:
+        return False
+    new_table = render_requirements_table(dependencies)
+    original = readme_path.read_text()
+    if not TABLE_PATTERN.search(original):
+        # README has no Requirements section in the expected shape — leave it
+        # alone rather than guess where to inject one. Real-world providers
+        # that have dependencies all carry this section; flag for visibility.
+        print(
+            f"[sync-provider-readme] {readme_path} has no Requirements table — 
skipping",
+            file=sys.stderr,
+        )
+        return False
+    updated = TABLE_PATTERN.sub(rf"\1{new_table}\n", original)
+    if updated == original:
+        return False
+    readme_path.write_text(updated)
+    print(f"[sync-provider-readme] updated {readme_path}")
+    return True
+
+
+def main(argv: list[str]) -> int:
+    changed = False
+    for arg in argv:
+        path = Path(arg)
+        if path.name != "pyproject.toml" or "providers/" not in 
path.as_posix():
+            continue
+        if sync_one(path):
+            changed = True
+    # prek detects modifications via git diff after the hook runs, so a zero
+    # exit code combined with file changes is the standard "files were
+    # modified by this hook" failure signal.
+    return 0 if not changed else 1
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv[1:]))

Reply via email to