On this page
Observe registration and runtime lifecycles๏
Prerequisite: configuration. Hooks attach observation to a
book or registration so call sites can keep using ordinary meld and lifecycle verbs.
Pick the boundary you need๏
Boundary |
Registration surface |
Saved demonstration |
|---|---|---|
Bind registration |
|
Check the reference, annotate its Spell, observe registration |
Spell creation |
|
Record construction events |
Meld |
|
Observe pre/post resolution |
Conduit |
The same book-keyed hook registration |
Observe creation and cleanup |
Links and contracts |
The same book-keyed hook registration |
Observe linking, pull, and sever |
Register callbacks before the lifecycle operation you intend to observe. For configuration-based Conduit/Meld hooks, a Book ID selects Book-specific events; omitting it supplies defaults. Bind hooks belong to the Book itself and use the three named stages below.
Follow a binding through its three stages๏
Intermediate 40 is the runnable bind-hook example. Its callbacks receive these subjects:
Stage |
Callback argument |
Timing and purpose |
|---|---|---|
|
The exact class, function or existing object passed to bind |
Before reflection; check the reference and raise to reject it |
|
The actual new |
Before publication; inspect it and add application metadata or tags |
|
That |
Inspect the registered result or record an application audit |
book.add_bind_hooks(
pre=[check_reference],
activation=[configure_spell],
post=[observe_registration],
)
Pass sequences of synchronous callbacks. Calls append in order; returning False
does not reject a reference, and returning another object does not replace it.
Raise an exception to reject. Melder wraps callback failures in HookExecutionError
and preserves their phase, hook_name, and original_exception.
Bind activation constructs/configures the Spell definition. It does not create
the application object. The older fluent creation-hook lesson runs callbacks during
Meld instead. Intermediate 40 proves this distinction: binding records pre โ activation
โ post, and a later meld("Report") leaves that bind-event list unchanged.
Configure once or adjust future bindings๏
For initial callbacks, call configuration.with_bind_hooks(...) before constructing
the Book. Each Book captures those seeds during construction. Runtime
book.add_bind_hooks(...) then appends to that Book's own registry.
book.clear_bind_hooks() removes all three stages from future bindings. Register
them again with add_bind_hooks; an empty sequence does not clear a stage.
The normal Conduit exposes the same add_bind_hooks and clear_bind_hooks methods.
These methods remain usable after configuration freezes, subject to the existing
rules for actually binding new definitions. Lessers cannot change their borrowed
Book's bind hooks through the Conduit facade.
An in-flight bind keeps one captured callback set; updates affect subsequent binds. Clearing does not erase metadata from earlier Spells or remove their creation hooks. Shared configuration does not turn runtime Bind-hook edits into cross-Book updates.
Post-bind runs after registration, before any enclosing transaction's final completion. A post-hook failure can leave the binding registered. The expert bind-hook lesson demonstrates that case alongside agent-supplied reference checks and changes to the actual Spell.
Keep the result separate from the observation๏
A hook records a moment in a lifecycle. The meld still supplies the resolved object. Use the meld-hook lesson's repeated resolutions and event counts as a small working pattern; use the conduit lesson when cleanup observation matters.
For the dynamic arc, read linking before the link-hook lesson. The latter observes the same owner/borrower operations; it adds no alternative sharing procedure.
The workflow in code๏
These are the core steps from the saved example. Use its complete linked script for all class definitions and setup.
1def main() -> None:
2 """Exercise the three stages and future-bind updates in one dynamic Book.
3
4 Contract:
5 Assertions distinguish raw class references, unpublished Spells and
6 completed registrations. Final cleanup releases the Book's callbacks.
7 """
8 events: list[str] = []
9
10 def check_reference(reference: object) -> None:
11 """Apply this application's class-only registration policy before reflection."""
12 if not inspect.isclass(reference):
13 raise TypeError("This example accepts class references only.")
14 events.append(f"pre:{reference.__name__}")
15
16 def configure_spell(spell: md.Spell) -> None:
17 """Annotate the actual new Spell before it becomes publicly discoverable."""
18 assert isinstance(spell, md.Spell)
19 assert book.find_spell_by_spell_id(spell.spell_id) is None
20 spell.metadata["reviewed_by"] = "application-bind-policy"
21 spell.tags.append("reviewed")
22 events.append(f"activation:{spell.spell_name}")
23
24 def observe_registration(spell: md.Spell) -> None:
25 """Verify the registered Spell is the same object activation annotated."""
26 assert book.find_spell_by_spell_id(spell.spell_id) is spell
27 assert spell.metadata["reviewed_by"] == "application-bind-policy"
28 events.append(f"post:{spell.spell_name}")
29
30 # Configuration seeds are captured when this Book is constructed.
31 configuration = md.SpellbookConfiguration().with_defaults()
32 configuration.with_bind_hooks(pre=[check_reference])
33 book = md.Spellbook(configuration=configuration)
34 conduit = None
35 try:
36 # Runtime setup appends stages to this Book's captured seed callbacks.
37 book.add_bind_hooks(activation=[configure_spell], post=[observe_registration])
38 report_id = book.bind(spell=Report, existence="unique")
39 assert events == ["pre:Report", "activation:Report", "post:Report"]
40 registered = book.find_spell_by_spell_id(report_id)
41 assert registered is not None
42 assert "reviewed" in registered.tags
43 print("bind order:", " -> ".join(events))
44
45 conduit = book.conjure(dynamic=True, name="bind-hook-basics")
46 assert conduit.meld("Report").render() == "report ready"
47 assert events == ["pre:Report", "activation:Report", "post:Report"]
48 print("meld returned the Report; bind callbacks did not run again")
49
50 # The normal Conduit facades the same Book-owned bind-hook registry.
51 conduit.clear_bind_hooks()
52 metrics_id = conduit.bind(spell=Metrics, existence="unique")
53 metrics_spell = book.find_spell_by_spell_id(metrics_id)
54 assert metrics_spell is not None
55 assert metrics_spell.metadata == {}
56 assert len(events) == 3
57 assert registered.metadata["reviewed_by"] == "application-bind-policy"
58 print("clear affected future binds; the earlier Spell kept its metadata")
59
60 conduit.add_bind_hooks(
61 pre=[check_reference], activation=[configure_spell], post=[observe_registration],
62 )
63 conduit.bind(spell=Notifier, existence="unique")
64 assert events[3:] == ["pre:Notifier", "activation:Notifier", "post:Notifier"]
65 print("re-added hooks:", " -> ".join(events[3:]))
66 finally:
67 if conduit is not None:
68 conduit.cleanup()
69 book.cleanup()
Runnable examples๏
Bind lifecycle hooks โ Intermediate 40
Registration hooks โ Intermediate 03
Config meld hooks โ Intermediate 32
Config conduit lifecycle hooks โ Intermediate 33
Config link and contract hooks โ Intermediate 34
All intermediate examples ยท Level contents ยท Full contents