Background
Users shouldn't need to memorise which API family (data/client/auth/notifier) an operation lives in. Flattening the whole surface onto cf was investigated and rejected: names are only unique at the gRPC-path level, not at the Python-attribute level a flattened cf.<name> would use.
Evidence from the generated code (54 services, 114 distinct method names):
- Method names collide heavily —
create/delete/get/update/get_all each appear on ~18–19 services, so cf.create(...) can't resolve to one RPC.
- Service names collide three ways —
general (all four families), process (client + data, different hosts, same 7 methods), version (auth + client, same 6 methods). A flattened cf.process would silently route to the wrong cluster host.
So the two-level cf.data.insert structure stays; this ticket delivers the ergonomics the flattening was reaching for, without the misrouting hazard. Design: §3.2 Why the family segment stays
Scope
- Wrong-family / typo errors. Accessing a plausible-but-wrong attribute on
cf (or on a family sub-client) raises a helpful error naming the correct home, e.g. cf.insert → "insert lives on cf.data, not on the client directly". Build a static name→family index from the generated API_FAMILIES at import time; no per-service hand maintenance.
- Discoverability.
dir(cf) lists the four families; the error text and __repr__ make the split obvious. Optionally a cf.help() / lookup that answers "which family has <service>?".
- Curated top-level shortcuts. Hoist a small, hand-picked set of unambiguous high-traffic entry points onto
cf (beyond the already-planned cf.collection() / cf.query() from §6). Candidates only where the name is globally unambiguous; everything else stays namespaced. List the chosen set in the PR for review — do not auto-hoist.
Acceptance criteria
Notes
Background
Users shouldn't need to memorise which API family (
data/client/auth/notifier) an operation lives in. Flattening the whole surface ontocfwas investigated and rejected: names are only unique at the gRPC-path level, not at the Python-attribute level a flattenedcf.<name>would use.Evidence from the generated code (54 services, 114 distinct method names):
create/delete/get/update/get_alleach appear on ~18–19 services, socf.create(...)can't resolve to one RPC.general(all four families),process(client + data, different hosts, same 7 methods),version(auth + client, same 6 methods). A flattenedcf.processwould silently route to the wrong cluster host.So the two-level
cf.data.insertstructure stays; this ticket delivers the ergonomics the flattening was reaching for, without the misrouting hazard. Design: §3.2 Why the family segment staysScope
cf(or on a family sub-client) raises a helpful error naming the correct home, e.g.cf.insert→ "insertlives oncf.data, not on the client directly". Build a static name→family index from the generatedAPI_FAMILIESat import time; no per-service hand maintenance.dir(cf)lists the four families; the error text and__repr__make the split obvious. Optionally acf.help()/ lookup that answers "which family has<service>?".cf(beyond the already-plannedcf.collection()/cf.query()from §6). Candidates only where the name is globally unambiguous; everything else stays namespaced. List the chosen set in the PR for review — do not auto-hoist.Acceptance criteria
ClappformError(orAttributeErrorsubclass) naming the correctcf.<family>.<service>pathAPI_FAMILIES, not hand-maintained; colliding names (general,process,version) list all owning families in the hint rather than guessing onedir(cf)returns the four families plus any curated shortcutscf-level shortcuts are an explicit allowlist; ambiguous names are never hoisteddir()contents, each curated shortcut resolves to the same object as its namespaced pathNotes
cf.data.insert...path remains the canonical, documented form; nothing here removes or renames it.