audit_trail.checks¶
Static checks of audited models, meant for CI.
check_models inspects the mapped classes of a registry and returns every
problem it finds instead of raising on the first one, so a single CI run
reports all of them, warnings included::
def test_audited_models() -> None:
errors = [i for i in check_models(Base) if i.level == "error"]
assert not errors, "\n".join(map(str, errors))
IssueCode
module-attribute
¶
IssueCode: TypeAlias = Literal[
"sensitive-column",
"json-in-place",
"relationship-both-sides",
"unknown-relationship",
]
What a ModelIssue is about.
IssueLevel
module-attribute
¶
How serious a ModelIssue is.
ModelIssue
dataclass
¶
One problem found by :func:check_models.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
IssueCode
|
What the problem is about. |
model |
type[Any]
|
The mapped class it was found on. |
attribute |
str
|
The attribute key it concerns. |
message |
str
|
A human-readable explanation, with the fix. |
check_models ¶
check_models(
base: registry | type[Any],
*,
allow_names: Collection[str] = (),
) -> list[ModelIssue]
Check the audited models of a registry for configuration mistakes.
Configures the registry's mappers first. Reports:
sensitive-column(error): a column of anAuditedmodel whose attribute key or database column name looks sensitive and that has no explicitinfo={"audit": ...}policy. A name looks sensitive when it containspasswordorpasswd, has one of the wordspwd,secret(s),token(s),credential(s),apikeyorencrypted, or ends in_key; words are split on_and camelCase. Names given toglobal_redactdo not count: they are an extra safety net, not a decision about the column.json-in-place(warning): an audited JSON column that is neither tracked bysqlalchemy.ext.mutablenor listed inAuditOptions.snapshot_on_load. Its in-place changes (obj.data["a"] = 1) are not detected.Mutabledetects them but leaves the old value unknown;snapshot_on_loadalone keeps the old value but only helps when the change is flagged (flag_modified).relationship-both-sides(error): both sides of one relationship (back_populatesorbackref) are tracked, throughAuditOptions.track_relationshipsortrack_relationships(); every membership change would be recorded twice.unknown-relationship(error):AuditOptions.track_relationshipsnames something that is not a collection relationship of the model (a missing name, a column or a scalar relationship).
Whether a column is Mutable is found by assigning {} and []
to the attribute of a throwaway instance created without __init__;
no session and no SQL are involved, but the model's own set
listeners and validators run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
registry | type[Any]
|
A |
required |
allow_names
|
Collection[str]
|
|
()
|
Returns:
| Type | Description |
|---|---|
list[ModelIssue]
|
The issues, sorted by model, attribute and code. Empty when there |
list[ModelIssue]
|
are none. |
Raises:
| Type | Description |
|---|---|
TypeError
|
|
FieldPolicyError
|
A column has an unknown audit policy. |