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

Download this collection ยท Source on GitHub

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.

More intermediate examples ยท Level guide

API contracts๏ƒ