> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensor9.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AWS Python SDK Guide

> Configure boto3 and Botocore to call a Cloud Adapter endpoint and preserve diagnostic context.

With boto3, your application continues to use the normal AWS service model and request dictionaries. Configure the endpoint for the process or for one client.

## Set it for the process

```bash theme={null}
export AWS_ENDPOINT_URL_S3="https://adapter.example.test"
```

Then create the client without `endpoint_url`:

```python theme={null}
import os
import boto3
from botocore.config import Config

session = boto3.Session(profile_name="adapter-test")
s3 = session.client(
    "s3",
    region_name=os.environ.get("AWS_REGION", "us-east-1"),
    config=Config(s3={"addressing_style": "path"}),
)
```

## Set it on one client

```bash theme={null}
export T9_CLOUD_ADAPTER_ENDPOINT="https://adapter.example.test"
```

```python theme={null}
import os
import boto3
from botocore.config import Config

session = boto3.Session(profile_name="adapter-test")
s3 = session.client(
    "s3",
    endpoint_url=os.environ["T9_CLOUD_ADAPTER_ENDPOINT"],
    region_name=os.environ.get("AWS_REGION", "us-east-1"),
    config=Config(
        s3={"addressing_style": "path"},
        retries={"mode": "standard", "max_attempts": 3},
        connect_timeout=5,
        read_timeout=30,
    ),
)
```

Use a separate `Session` or dependency-injected client for Cloud Adapter. Avoid changing a process-wide endpoint behind unrelated AWS clients.

An explicit `endpoint_url` takes precedence over `AWS_ENDPOINT_URL_S3`.

## Verify object behavior

```python theme={null}
import time

bucket = os.environ["TEST_BUCKET"]
key = f"python/{int(time.time())}-hello.txt"
expected = b"hello through Cloud Adapter\n"

s3.put_object(Bucket=bucket, Key=key, Body=expected, ContentType="text/plain")
head = s3.head_object(Bucket=bucket, Key=key)
downloaded = s3.get_object(Bucket=bucket, Key=key)["Body"].read()

assert downloaded == expected
assert head["ContentLength"] == len(expected)

s3.delete_object(Bucket=bucket, Key=key)
```

Inspect the corresponding native target object after `put_object`. If the application depends on metadata, ETags, version IDs, ranges, conditionals, or multipart uploads, assert those behaviors separately against the [S3 target profile](/cloud-adapter/service-catalog/aws/databases-storage/s3).

## Distinguish service and transport failures

```python theme={null}
from botocore.exceptions import ClientError, EndpointConnectionError, ReadTimeoutError

try:
    s3.head_object(Bucket=bucket, Key="missing-key")
except ClientError as error:
    response = error.response
    print({
        "code": response.get("Error", {}).get("Code"),
        "status": response.get("ResponseMetadata", {}).get("HTTPStatusCode"),
        "request_id": response.get("ResponseMetadata", {}).get("RequestId"),
    })
except (EndpointConnectionError, ReadTimeoutError) as error:
    print({"transport_error": type(error).__name__, "message": str(error)})
```

A `ClientError` means an HTTP service response reached Botocore. A connection or read timeout may have no response metadata at all. After a timed-out write, check target state before deciding whether to retry.

## Production checklist

* Pin boto3 and Botocore versions together.
* Keep the endpoint, region, retry policy, and credentials explicit.
* Reuse clients across requests.
* Do not log `Authorization`, signed URLs, secret values, or object bodies.
* Capture `ResponseMetadata` and [Cloud Adapter diagnostic headers](/cloud-adapter/debugging/response-headers).
* Add integration tests for paginators, waiters, retries, and error codes used by the application.

Backend credentials are configured on Cloud Adapter. They do not belong in the Python application's AWS session.
