Hedronite · Dev Lesson · Polyglot-Dev / Python · Sun 2026-09-13

Python google-cloud-storage bucket policy inventory — reload, pages, rules

Reload the bucket. Thin list stubs lie about lifecycle.

Lesson Class: Dev (Python + google-cloud-storage)
Cloud Referent: GCS bucket lifecycle / retention / versioning fields
Paired Ops: Python GCS lifecycle and retention census
Paired Cert: GCP PCA Cloud Storage classes and retention
Paired Go: Go BucketAttrs lifecycle inventory
Grounding: Bootcamp Ch.3 · Gift Python for DevOps
List
Page list_buckets; never one-shot assume.
Reload
Fill lifecycle and retention.
Normalize
action, age, matchesStorageClass.
A stub without reload is a polite lie.

Reload the bucket. Thin list stubs lie about lifecycle.

§I — Frame

Ops asks for three shelves on every bucket: class/lifecycle, retention, versioning. Dev builds the inventory that prints them with google.cloud.storage. Stay next to the GCP referent. Leave Protocol plugins (09-10) and generator expressions (09-01) on their shelves.

§II — Client shape

from google.cloud import storage
from google.api_core import exceptions as gexc

def rule_row(rule):
    action = rule.get("action") or {}
    cond = rule.get("condition") or {}
    return {
        "action": action.get("type"),
        "storage_class": action.get("storageClass"),
        "age": cond.get("age"),
        "matches_storage_class": cond.get("matchesStorageClass"),
        "is_live": cond.get("isLive"),
        "num_newer_versions": cond.get("numNewerVersions"),
    }

def inventory(project, page_size=100):
    client = storage.Client(project=project)
    out = []
    iterator = client.list_buckets(max_results=page_size)
    for bucket in iterator:
        try:
            bucket.reload()
        except gexc.Forbidden:
            out.append({"name": bucket.name, "error": "403_reload"})
            continue
        out.append({
            "name": bucket.name,
            "storage_class": bucket.storage_class,
            "versioning": bool(bucket.versioning_enabled),
            "retention_period": bucket.retention_period,
            "retention_locked": bool(bucket.retention_policy_locked),
            "lifecycle": [rule_row(r) for r in (bucket.lifecycle_rules or [])],
        })
    return out

list_buckets pages. Do not assume one page is the project. reload() is mandatory before reading lifecycle_rules or retention fields. A 403 on reload is a row, not a crash.

§III — Rule normalization

Chapter-3 actions arrive as dict-like rows. Normalize action.type, action.storageClass, condition.age, condition.matchesStorageClass. Print numNewerVersions when versioning cleanup rules appear. Keep raw keys out of the CLI table when you can name the shelf.

§IV — Boundaries

  1. Do not bucket.patch() from inventory.
  2. Do not enable versioning "to be safe" inside the tool.
  3. Project identity is part of the row. Empty list means empty project view, not org truth.
  4. Soft-delete policy fields vary by library version; use getattr and print unknown as null.

§V — Closing

Match Ops WARN semantics: missing lifecycle on cold data, Delete beating retention needs, versioning without noncurrent cleanup. Maghrib folds Go items into the Dev quiz later.

Related