Debugging refusals¶
When something is refused¶
A refusal's message says what was refused, by name, which rule refused it, and what would allow it:
Refused by policy: updating Labor Cost (6) in Job Tasks (bqtasks). No rule allows it. (rule: none)
Nothing was sent. To allow it: add "Labor Cost" to an allow.update("Job Tasks", ...) rule.
| What happened | Exception | What's left behind |
|---|---|---|
| A request the policy doesn't allow | PolicyRefusedError, whose decision says why |
Nothing was sent |
| A table or field the policy hides is named | UnknownTableError or UnknownFieldError |
Nothing was sent |
| A later batch of a write was refused | WriteError |
Earlier batches were written, and are in error.result |
| A policy that names a table, field, or class that doesn't exist | ConfigurationError, listing every one |
It wasn't bound |
A Policy that names tables passed to a client without binding |
ConfigurationError |
The client wasn't created |
A realm, token, or base_url that can't be used, such as http:// to another host |
ConfigurationError |
The client wasn't created |
from_env() with a variable missing or empty |
ConfigurationError |
The client wasn't created |
Asking the policy¶
A bound policy answers questions without sending anything:
from pyquickbase import Policy, Quickbase, allow
policy = Policy(rules=[allow.read("Tasks"), allow.update("Tasks", ["Status", "Notes"])])
with Quickbase.from_env() as setup:
bound = policy.bind(setup.app("bq1234567"))
bound.allows("update", "Tasks", field="Amount").allowed # False
print(bound.explain("lookup", "Tasks", source=("Tasks", "Amount")))
bound.offers("Tasks").updatable # What an operation would offer
Each returns or explains a Decision, with allowed, the layer that decided, the rule as
written, a reason, and a hint saying what would allow it. decide() is the only code that
decides anything, and what operations offer comes from asking it about each field and verb, so a
model is never offered what the policy refuses.