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

book.add_bind_hooks(pre=..., activation=..., post=...)

Check the reference, annotate its Spell, observe registration

Spell creation

SpellBinder.with_pre_hook, with_activation_hook, with_post_hook

Record construction events

Meld

configuration.add_hook(book.id, name, callback)

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

pre

The exact class, function or existing object passed to bind

Before reflection; check the reference and raise to reject it

activation

The actual new Spell

Before publication; inspect it and add application metadata or tags

post

That Spell after registration

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๏ƒ

All intermediate examples ยท Level contents ยท Full contents

Canonical page source