Hooks
Python Hooks
Define Python lifecycle hooks with runtime context, providers, SQL access, and skips.
Python hooks run model lifecycle logic that needs Python control flow, providers, warehouse queries, logging, or explicit skip decisions.
For shared lifecycle ordering, failure timing, naming, and identity rules, see the Hooks overview.
Define a Python hook
Section titled “Define a Python hook”SQLBuild discovers decorated functions recursively from .py files under hooks/python/ and
under scoped <folder>/_sqlbuild/hooks/python/ and <folder>/_sqlbuild/_hooks/python/ roles; see
Hooks for who can call each:
hooks/python/notifications.py
from sqlbuild.hooks import hook
@hookdef notify_complete(ctx, channel="#data-builds"): """Notify complete.""" ctx.log( f"Notify {channel}: " f"{ctx.model_name} completed during {ctx.phase}" )By default, the hook name is the function name. The decorator accepts optional name, description, and reads arguments. A hook needs a description: pass description= or give the function a docstring (Descriptions). A Python file may define multiple decorated hooks.
Declared reads
Section titled “Declared reads”A hook that queries other models, sources, or seeds declares them with reads and resolves each
one with ctx.relation(...):
from sqlbuild.hooks import hookfrom sqlbuild.refs import model
@hook(reads=model("dim_customers"))def refresh_lookup(ctx): """Refresh lookup.""" customers = ctx.relation(model("dim_customers")) ctx.execute_sql(f"CREATE OR REPLACE TABLE customer_lookup AS SELECT * FROM {customers}")reads accepts one reference or a list of model(...), source(...), and seed(...)
references from sqlbuild.refs. ctx.relation(...) returns the adapter-qualified relation for
the current run, following deferral like SQL references do. Resolving a reference that is not
declared raises, as it does for Python nodes.
Declared reads order the build: a model that runs the hook waits for the resources the hook reads
when both are in the build. In a partial build, unselected reads are not built, and the hook reads
their existing relations. A hook cannot read a resource built from the model that runs it, because
that model would have to wait for its own dependants; that fails to compile with P007. Reading
an unknown model, source, or seed fails to compile. Use ctx.destination for the hook’s own model.
The Dagster integration does not add declared reads to its asset graph, the same as reads of
attached audits. Within one sqb build the ordering holds; when Dagster runs the hooked model in a
separate build from the resources it reads, the hook reads their existing relations.
Declaring reads does not change who may call the hook: the model must still see the hook under the usual scope rules.
SQL hooks declare reads by referencing resources in their SQL; see Read models, sources, and seeds.
Literal project relation names in ctx.query() and ctx.execute_sql() SQL fail to compile, and
dynamically built names produce run-time warnings; see
Hard-coded relation names.
Files named __init__.py or beginning with _ are skipped. Imported decorated functions are not registered again from the importing module.
Invoke a Python hook
Section titled “Invoke a Python hook”Reference the hook by name and optionally pass keyword arguments:
MODEL ( description "Example model", materialized table, post_hooks [ python( "notify_complete", channel: "#model-alerts", ), ],);
SELECT 1 AS idPython hook arguments are ordinary configuration values and are not SQL-expanded. An argument containing @@CTX:... or @py.name() reaches the function unchanged.
Signature validation
Section titled “Signature validation”Unknown hooks, unknown keyword arguments, missing required arguments, required positional-only arguments, and arguments that conflict with context or provider injection fail compilation. A function with **kwargs can accept additional configured arguments.
Python hooks must return None or ctx.skip(...). Any other return value fails execution.
Hook context
Section titled “Hook context”Python hooks declare a HookContext parameter named ctx, context, _ctx, or hook_context. It need not be the first parameter when providers or configured arguments are also present:
| Field | Description |
|---|---|
ctx.model_name |
Model being built |
ctx.phase |
pre_hooks or post_hooks |
ctx.hook_name |
Invoked hook name |
ctx.hook_index |
Zero-based position in the authored hook list |
ctx.run_id |
Current run ID |
ctx.target |
Active target |
ctx.vars |
Effective project variables |
ctx.destination |
Destination relation metadata |
ctx.relation(ref) |
Adapter-qualified relation for a declared read |
ctx.adapter_name |
Active adapter name |
ctx.adapter |
Adapter instance |
ctx.connection |
Live connection |
ctx.execute_sql(sql) |
Execute SQL |
ctx.query(sql) |
Execute SQL and return rows |
ctx.log(message) |
Write run output |
ctx.skip(reason="...", mode="soft") |
Return a soft or hard skip result; arguments are keyword-only |
ctx.providers |
Access discovered providers |
Providers
Section titled “Providers”Providers may be injected into Python hook parameters by name or accessed through ctx.providers. They are resolved lazily using the same lifecycle as loaders, tasks, assets, and checks. SQL hooks cannot use providers because they are compiled SQL statements rather than Python callables.
from sqlbuild.hooks import hook
@hookdef notify_complete(ctx, slack_notifier): """Notify complete.""" slack_notifier.send( f"Model {ctx.model_name} built successfully" )Skips and failures
Section titled “Skips and failures”Returning ctx.skip(...) stops the remaining hooks in the current phase. Runtime exceptions fail that lifecycle phase. See Lifecycle and failure timing for the effects of pre-hook and post-hook skips, soft and hard modes, and failures after warehouse mutation.
Identity and diagnostics
Section titled “Identity and diagnostics”Python hook invocations and version hashes participate in model identity as described in Names and identity. Executed hooks also record their own fingerprints after successful completion or an explicit skip.
Compilation reports unknown hooks and invalid signatures with the model name and indexed invocation label, such as post_hooks[1] python("notify_complete"). Runtime output preserves the authored index and hook name. Exceptions and unsupported return values fail with the Python hook identity attached.