On this page
Config disposal and the frozen law๏
๐ก Intermediate ยท Lesson 30
SpellbookConfiguration, knob by knob - part 1: DISPOSAL. Two set-once properties describe teardown policy: disposal (bool) - stored flag; matched names drive calls disposal_method_names (list) - the ordered book-wide vocabulary enforce_priority_disposal_methods places the book block first when True or last when False (default). The book owns shared names in both modes. Both are IDEMPOTENT properties: set once, immutable after - the teardown story of a world must not change mid-flight. And the whole configuration FREEZES at conjure: any set_property after that refuses. Configuration is a pre-flight surface, period.
Before you run๏
Use the Intermediate guide for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules.
Run the saved script๏
python UX_and_AIX_experiences/02_intermediate/30_config_disposal_and_the_frozen_law.py
py -3.14t UX_and_AIX_experiences/02_intermediate/30_config_disposal_and_the_frozen_law.py
Public surface๏
SpellbookConfiguration.set_property, with_enforce_priority_disposal_methods, bind, meld, cleanup
Code๏
1"""
2TIER: intermediate (30)
3GOAL: SpellbookConfiguration, knob by knob - part 1: DISPOSAL.
4 Two set-once properties describe teardown policy:
5 disposal (bool) - stored flag; matched names drive calls
6 disposal_method_names (list) - the ordered book-wide vocabulary
7 enforce_priority_disposal_methods places the book block first when
8 True or last when False (default). The book owns shared names in both modes.
9 Both are IDEMPOTENT properties: set once, immutable after - the
10 teardown story of a world must not change mid-flight. And the
11 whole configuration FREEZES at conjure: any set_property after
12 that refuses. Configuration is a pre-flight surface, period.
13SURFACE EXERCISED: SpellbookConfiguration.set_property,
14 with_enforce_priority_disposal_methods, bind, meld, cleanup
15"""
16import melder as md
17
18
19class PooledThing:
20 """Expose observable ordered cleanup methods without a Melder-specific base class."""
21
22 def __init__(self) -> None:
23 """Start open with no disposal calls recorded."""
24 self.closed = False
25 self.calls: list[str] = []
26
27 def close(self) -> None:
28 """Mark closed and record the method's actual invocation position."""
29 self.closed = True
30 self.calls.append("close")
31
32 def flush(self) -> None:
33 """Record the configured flush step."""
34 self.calls.append("flush")
35
36 def release(self) -> None:
37 """Record the spell-only release step."""
38 self.calls.append("release")
39
40
41def demonstrate_priority(priority: bool) -> None:
42 """Show that the book owns shared names regardless of front/back placement."""
43 config = md.SpellbookConfiguration()
44 config.with_disposal_method_names(["flush", "close"])
45 config.with_enforce_priority_disposal_methods(priority)
46 config.with_defaults().finalize()
47 book = md.Spellbook(configuration=config)
48 book.bind(
49 spell=PooledThing, existence="many",
50 disposal_method_names=["close", "release", "flush", "missing"],
51 )
52 conduit = book.conjure()
53 resource = conduit.meld("PooledThing")
54 conduit.cleanup()
55 expected = ["flush", "close", "release"] if priority else ["release", "flush", "close"]
56 assert resource.calls == expected
57 print("book block first" if priority else "book block last", resource.calls)
58 book.cleanup()
59
60
61def main() -> None:
62 """Verify configuration timing and both ordered disposal arrangements."""
63 # CONFIGURE, THEN LOCK - the order is the lesson.
64 # The disposal pair is SET-ONCE. A book you construct bare has already
65 # taken the standard default set, and that set is COMPLETE - it fills
66 # every required property, disposal included. Complete means finished:
67 # there is no room left to write disposal into it afterwards.
68 # So you pick one path. Take the defaults and live with them, or state
69 # your own policy up front and hand it to the book. This lesson states
70 # its own.
71 config = md.SpellbookConfiguration()
72 config.set_property("disposal", True)
73 config.set_property("disposal_method_names", ["close"])
74 config.set_property("phase_scheduler_workers_per_spellbook", 5)
75 config.set_property(
76 "phase_scheduler_barrier_timeout_milliseconds", 60000)
77
78 book = md.Spellbook(configuration=config)
79 book.bind(spell=PooledThing, existence="unique")
80
81 # IDEMPOTENT LAW: these two are set-once. A second set refuses.
82 try:
83 config.set_property("disposal", False)
84 print("idempotent re-set unexpectedly succeeded")
85 except Exception as err:
86 print("idempotent re-set refused:", type(err).__name__)
87
88 conduit = book.conjure()
89 thing = conduit.meld("PooledThing")
90 conduit.cleanup()
91 print("disposal vocabulary fired at cleanup:", thing.closed)
92
93 # FREEZE LAW: conjure froze the whole configuration.
94 try:
95 config.set_property("phase_scheduler_workers_per_spellbook", 2)
96 print("post-conjure set unexpectedly succeeded")
97 except Exception as err:
98 print("post-conjure set refused:", type(err).__name__)
99 book.cleanup()
100 demonstrate_priority(False)
101 demonstrate_priority(True)
102
103
104if __name__ == "__main__":
105 main()
Check the outcome๏
The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here.