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

shahar1 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 baac5581c6c Stop creating AWS clients when importing the docs 
publisher (#76658)
baac5581c6c is described below

commit baac5581c6c4b79edda19dce13e93d6398b42935
Author: Shahar Epstein <[email protected]>
AuthorDate: Fri Nov 13 14:49:11 2026 +0200

    Stop creating AWS clients when importing the docs publisher (#76658)
    
    Importing publish_docs_to_s3 made boto3 resolve credentials, so the import
    failed on any machine whose AWS profile cannot be loaded. A profile that 
uses
    `aws login` fails with botocore's MissingDependencyException unless
    botocore[crt] is installed, and that stopped 
test_release_management_commands.py
    from being collected, although none of its tests talk to AWS.
    
    Claude-Session: https://claude.ai/code/session_01BVx6g6v9hW7wi1tV4bqvrp
---
 .../src/airflow_breeze/utils/publish_docs_to_s3.py | 28 +++++++++++++---------
 dev/breeze/tests/test_publish_docs_to_s3.py        |  9 +++++++
 2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/dev/breeze/src/airflow_breeze/utils/publish_docs_to_s3.py 
b/dev/breeze/src/airflow_breeze/utils/publish_docs_to_s3.py
index e6722be51ef..9f7ae59c949 100644
--- a/dev/breeze/src/airflow_breeze/utils/publish_docs_to_s3.py
+++ b/dev/breeze/src/airflow_breeze/utils/publish_docs_to_s3.py
@@ -21,7 +21,7 @@ import os
 import pathlib
 import subprocess
 import sys
-from functools import cached_property
+from functools import cache, cached_property
 
 import boto3
 
@@ -46,8 +46,14 @@ NON_SHORT_NAME_PACKAGES = [
 ]
 
 
-s3_client = boto3.client("s3")
-cloudfront_client = boto3.client("cloudfront")
+@cache
+def get_s3_client():
+    return boto3.client("s3")
+
+
+@cache
+def get_cloudfront_client():
+    return boto3.client("cloudfront")
 
 
 class VersionError:
@@ -140,7 +146,7 @@ class S3DocsPublish:
 
     def doc_exists(self, s3_bucket_doc_location: str) -> bool:
         bucket, key = self.get_bucket_key(s3_bucket_doc_location)
-        response = s3_client.list_objects_v2(Bucket=bucket, Prefix=key)
+        response = get_s3_client().list_objects_v2(Bucket=bucket, Prefix=key)
 
         return response["KeyCount"] > 0
 
@@ -264,7 +270,7 @@ class S3DocsPublish:
         List 'directories' (prefixes) in an S3 path using boto3.
         """
         bucket, prefix = self.get_bucket_key(s3_path.rstrip("/"))
-        paginator = s3_client.get_paginator("list_objects_v2")
+        paginator = get_s3_client().get_paginator("list_objects_v2")
         result = []
         for page in paginator.paginate(Bucket=bucket, Prefix=prefix + "/", 
Delimiter="/"):
             for cp in page.get("CommonPrefixes", []):
@@ -300,7 +306,7 @@ class S3DocsPublish:
         bucket, _ = self.get_bucket_key(self.destination_location)
 
         console_print("[info]Uploading packages-metadata.json to S3\n")
-        s3_client.put_object(
+        get_s3_client().put_object(
             Bucket=bucket,
             Key="manifest/packages-metadata.json",
             Body=json.dumps(all_packages_infos, indent=2),
@@ -311,7 +317,7 @@ class S3DocsPublish:
         console_print(
             f"[info]Invalidating CloudFront cache for the uploaded files: 
distribution id {distribution_id}\n"
         )
-        cloudfront_client.create_invalidation(
+        get_cloudfront_client().create_invalidation(
             DistributionId=distribution_id,
             InvalidationBatch={
                 "Paths": {
@@ -399,7 +405,7 @@ class S3DocsPublish:
    <body></body>
 </html>"""
 
-        s3_client.put_object(
+        get_s3_client().put_object(
             Bucket=bucket,
             Key=s3_key,
             Body=html_body,
@@ -449,7 +455,7 @@ def _publish_single_schema(
     destination = 
f"{destination_location.rstrip('/')}/{schema_type}/{version}.json"
     bucket, key = S3DocsPublish.get_bucket_key(destination)
 
-    already_published = s3_client.list_objects_v2(Bucket=bucket, 
Prefix=key).get("KeyCount", 0) > 0
+    already_published = get_s3_client().list_objects_v2(Bucket=bucket, 
Prefix=key).get("KeyCount", 0) > 0
     if already_published and not overwrite:
         console_print(
             f"[info]Skipping {destination} — version {version} already 
published "
@@ -462,7 +468,7 @@ def _publish_single_schema(
         return False
 
     console_print(f"[info]Publishing {schema_type} version {version} to 
{destination}\n")
-    s3_client.put_object(
+    get_s3_client().put_object(
         Bucket=bucket,
         Key=key,
         Body=schema_path.read_bytes(),
@@ -479,7 +485,7 @@ def _invalidate_schemas_cache(destination_location: str) -> 
None:
     console_print(
         f"[info]Invalidating CloudFront cache for {invalidation_path}: 
distribution id {distribution_id}\n"
     )
-    cloudfront_client.create_invalidation(
+    get_cloudfront_client().create_invalidation(
         DistributionId=distribution_id,
         InvalidationBatch={
             "Paths": {"Quantity": 1, "Items": [invalidation_path]},
diff --git a/dev/breeze/tests/test_publish_docs_to_s3.py 
b/dev/breeze/tests/test_publish_docs_to_s3.py
index 08fe58b7f53..3707aa2127f 100644
--- a/dev/breeze/tests/test_publish_docs_to_s3.py
+++ b/dev/breeze/tests/test_publish_docs_to_s3.py
@@ -17,13 +17,22 @@
 
 from __future__ import annotations
 
+import runpy
 from unittest.mock import MagicMock, PropertyMock, mock_open, patch
 
 import pytest
 
+from airflow_breeze.utils import publish_docs_to_s3
 from airflow_breeze.utils.publish_docs_to_s3 import S3DocsPublish, VersionError
 
 
+@patch("boto3.client")
+def test_import_creates_no_aws_clients(mock_client):
+    runpy.run_path(publish_docs_to_s3.__file__)
+
+    mock_client.assert_not_called()
+
+
 class TestPublishDocsToS3:
     def setup_method(self):
         self.publish_docs_to_s3 = S3DocsPublish(

Reply via email to