Module trase.tools.r2

R2 (Cloudflare object storage) connection helpers for Trase Python scripts.

See :mod:trase.tools.r2.connection for usage — storage_options() for pandas/polars, attach_duckdb_secret() for DuckDB, wrangler_env() for shelling out to the wrangler CLI (preferred for one-off object get/put), cloudflare_client() for bucket-level admin via the official SDK, and client() (boto3) for anything else — all backed by Secrets Manager-vaulted R2 tokens rather than locally-held credentials.

Sub-modules

trase.tools.r2.connection

Access the Cloudflare R2 bucket (trase-r2) via Secrets Manager-vaulted tokens …

Functions

def api_token(role: Role = 'ro') ‑> str

The Cloudflare API token (cfut_…) for role.

Same secret, same bucket restriction as the S3 Access Key/Secret pair from :func:client()/:func:storage_options() — but not usable for object get/put via this route: Cloudflare's REST API (what wrangler and the official cloudflare Python package speak) 403s on object writes for anything less than an Admin-tier token, and ours are deliberately bucket-scoped "Object Read & Write" (https://github.com/cloudflare/workers-sdk/issues/9235). Only useful if you separately hold/create an Admin-tier token — see :func:cloudflare_client().

def attach_duckdb_secret(con, role: Role = 'ro', name: str = 'trase_r2') ‑> str

Create a TYPE r2 DuckDB secret named name on con. Returns name.

Lets callers then address objects as r2://<bucket>/<key> directly — DuckDB derives the endpoint from ACCOUNT_ID itself.

def bucket() ‑> str
def client(role: Role = 'ro')

A boto3 S3 client configured against the R2 endpoint with the resolved role's credentials.

The way to actually read/write objects — pandas/polars (:func:storage_options()) and this function both go through the S3-compatible API, which is the only one our bucket-scoped tokens work against for object operations (see :func:api_token()).

def cloudflare_client(role: Role = 'ro')

The official cloudflare Python SDK client, authenticated for role.

Only useful for R2 bucket-level administration (CORS, lifecycle rules, custom domains, bucket create/delete) — Cloudflare's REST API has no object-level get/put/list at all (those stay on :func:client() / :func:storage_options(), S3-compatible), and even bucket admin needs an Admin-tier token: our tokens are bucket-scoped "Object Read & Write" and will 403 here (https://github.com/cloudflare/workers-sdk/issues/9235).

def endpoint_url() ‑> str
def storage_options(role: Role = 'ro') ‑> dict

storage_options() for pandas/polars (via s3fs) reads/writes against R2.

def uri(key: str, scheme: "Literal['s3', 'r2']" = 's3') ‑> str

Build an s3:// (boto3/pandas/polars) or r2:// (native DuckDB) URI for key.

def wrangler_env(role: Role = 'ro') ‑> dict[str, str]

Env vars for shelling out to the wrangler CLI.

Merge into a subprocess call, e.g.::

subprocess.run(
    ["npx", "wrangler", "r2", "bucket", "cors", "set", bucket(), "--rules", path],
    env={**os.environ, **wrangler_env("rw")}, check=True,
)

Not for object get/putwrangler r2 object … needs an Admin-tier token and 403s with ours (bucket-scoped "Object Read & Write"); use :func:storage_options()/:func:attach_duckdb_secret()/ :func:client() (S3-compatible API) for that instead. This is only useful for wrangler r2 bucket() bucket-admin subcommands, and only if the resolved token happens to be Admin-tier.