On this page
Purge unneeded objects๏
๐ก Intermediate ยท Lesson 39
Release objects you no longer need while their scope keeps working. This job buffer declares close as its disposal method, so Melder retains each many creation until explicit disposal. Pass an instance with purge_all=False to retire only that object. The default purge_all=True retires all retained objects for the selected binding in that scope.
Purge returns the removed count, keeps the registration available, and lets later melds create fresh objects. A SpellSpace can purge its own objects without touching the conduit. Application-held Python references still point to the old objects: stop using their resources after disposal.
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/39_purge_unneeded_objects.py
py -3.14t UX_and_AIX_experiences/02_intermediate/39_purge_unneeded_objects.py
Public surface๏
md.Spellbook.bind, md.Existence.many, md.Conduit.meld, md.Conduit.purge, md.Conduit.enter_spellspace, md.SpellSpace.meld, md.SpellSpace.purge, md.Conduit.cleanup
Code๏
1"""
2TIER: intermediate (39)
3GOAL: Release objects you no longer need while their scope keeps working.
4 This job buffer declares close as its disposal method, so Melder retains
5 each many creation until explicit disposal. Pass an instance with
6 purge_all=False to retire only that object. The default purge_all=True
7 retires all retained objects for the selected binding in that scope.
8
9 Purge returns the removed count, keeps the registration available, and
10 lets later melds create fresh objects. A SpellSpace can purge its own
11 objects without touching the conduit. Application-held Python references
12 still point to the old objects: stop using their resources after disposal.
13SURFACE EXERCISED: md.Spellbook.bind, md.Existence.many, md.Conduit.meld,
14 md.Conduit.purge, md.Conduit.enter_spellspace,
15 md.SpellSpace.meld, md.SpellSpace.purge, md.Conduit.cleanup
16"""
17import melder as md
18
19
20class WorkBuffer:
21 """Own a small job payload with observable, explicit disposal.
22
23 The payload belongs to this object and is released by close. The public
24 closed flag and close_calls counter remain as lesson diagnostics, so the
25 example can check disposal without reaching into Melder's private state.
26 """
27
28 def __init__(self) -> None:
29 """Allocate an open buffer; no file, network or background work is started."""
30 self._payload: bytearray = bytearray(128)
31 self.closed: bool = False
32 self.close_calls: int = 0
33
34 def close(self) -> None:
35 """Release the payload once and count each actual disposal invocation.
36
37 Repeated calls leave the resource closed. Diagnostic fields remain
38 readable; the owned payload field is removed after its contents clear.
39 """
40 self.close_calls += 1
41 if self.closed:
42 return
43 self._payload.clear()
44 del self._payload
45 self.closed = True
46
47
48def main() -> None:
49 """Verify early disposal, retained siblings, remelding and scope isolation.
50
51 The book and conduit are cleaned in finally blocks. Each assertion checks
52 an observable removal count, object identity or disposal callback result.
53 """
54 book = md.Spellbook()
55 try:
56 # many creates a fresh object each time. Declared disposal makes these
57 # buffers retained resources that Melder can retire through purge.
58 book.bind(
59 spell=WorkBuffer,
60 existence=md.Existence.many,
61 disposal_method_names=["close"],
62 )
63 conduit = book.conjure()
64 try:
65 first: WorkBuffer = conduit.meld("WorkBuffer")
66 second: WorkBuffer = conduit.meld("WorkBuffer")
67 third: WorkBuffer = conduit.meld("WorkBuffer")
68
69 # The first job finished. Keep the other two objects alive.
70 removed = conduit.purge(first, purge_all=False)
71 assert removed == 1
72 assert first.closed and first.close_calls == 1
73 assert not second.closed and not third.closed
74 assert conduit.purge(first, purge_all=False) == 0
75 print("one finished buffer removed; its siblings remain:", removed)
76
77 # A name selects the binding. Default True removes all of its
78 # remaining retained objects in this conduit, not just one.
79 removed = conduit.purge("WorkBuffer")
80 assert removed == 2
81 assert second.closed and third.closed
82 assert conduit.purge("WorkBuffer") == 0
83 print("remaining buffers removed by name:", removed)
84
85 # The binding and the conduit still work after the bucket is empty.
86 replacement: WorkBuffer = conduit.meld("WorkBuffer")
87 assert replacement is not first and not replacement.closed
88 print("the same registration creates a fresh buffer:", not replacement.closed)
89
90 with conduit.enter_spellspace() as space:
91 local: WorkBuffer = space.meld("WorkBuffer")
92 # An instance does not grant access to a different scope.
93 assert space.purge(replacement, purge_all=False) == 0
94 assert conduit.purge(local, purge_all=False) == 0
95 assert space.purge(local, purge_all=False) == 1
96 assert local.closed and not replacement.closed
97 local_replacement: WorkBuffer = space.meld("WorkBuffer")
98 assert local_replacement is not local and not local_replacement.closed
99
100 # Managed space exit closes its remaining buffer, not the root's.
101 assert local_replacement.closed and not replacement.closed
102 print("spell-space purge and exit leave the conduit buffer open:", not replacement.closed)
103 finally:
104 conduit.cleanup()
105
106 # Purged objects are no longer retained for later scope cleanup.
107 resources = (first, second, third, replacement, local, local_replacement)
108 assert all(resource.closed for resource in resources)
109 assert all(resource.close_calls == 1 for resource in resources)
110 print("every buffer was disposed exactly once:", len(resources))
111 finally:
112 book.cleanup()
113
114
115if __name__ == "__main__":
116 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.