Static governance scan (no run required)
Conformare normally captures governance at runtime – a describe() / risk() region has to execute to be recorded. That is ideal for the live report (it sees the real lineage and data), but it means a context in a branch that didn’t run, or a module nobody imported, is invisible. The static scan reads the source instead: it parses your .py files and extracts every declared context and risk without executing anything.
scan = cf.scan_governance("src/") # a file, a directory, or a list
# {'files_scanned': 12, 'contexts': [...], 'risks': [...], 'errors': [...]}
cf.scan_governance_report("src/", "governance.html") # a self-contained HTML report
Each context and risk carries its file and line, so you can build a governance report for a whole repository that has never been run.
What it matches
It recognises only the governance forms, so ordinary calls are never mistaken for it:
with cf.describe(...)/with cf.risk(...)blocks (including a combinedwith cf.describe(...), cf.risk(...):),@cf.describe(...)/@cf.risk(...)decorators on functions,- a
risks=argument on adescribe(...).
Because it only looks at with-items, decorators and risks=, a method call like df.describe() is never picked up as governance. Only literal arguments are read – a static scan can’t evaluate a computed value – so keep ids, severities and owners as literals if you want them discoverable this way.
Runtime vs static – two views, one model
Runtime capture (to_html) | Static scan (scan_governance) | |
|---|---|---|
| Needs the code to run | yes | no |
| Sees actual lineage / profiles / data | yes | no (declarations only) |
| Sees branches/modules that didn’t run | no | yes |
| Sees computed (non-literal) governance | yes | no (literals only) |
They are complementary: the runtime report is the record of what a run did; the static scan is the inventory of what the code declares, across everything – which is what you want for repo -wide discovery and for gating in CI.
Gating in CI
Because the scan needs nothing to run, it fits a pull-request check. For example, fail the build if any declared risk is missing a severity, or any context is unowned:
import sys, conformare as cf
scan = cf.scan_governance("src/")
problems = [r for r in scan["risks"] if not r["severity"]]
problems += [c for c in scan["contexts"] if not c["owner"]]
if problems:
print("governance gaps:", problems)
sys.exit(1)
Drop that in a GitHub Action step and the convention becomes enforced, the missing enforcement gap versus declarative tools, without ever running the pipeline.