On this page

Managed spell reset methods๏ƒ

๐ŸŸก Intermediate ยท Lesson 45

Hand the next caller a clean object - managed_spell_reset_methods. A pooled object is reused, so whatever the last holder left in it would leak to the next one. Name its reset methods at bind: zero-argument methods the pool calls, in the order given, on EVERY release, before the object is idle again. The next lease then gets the same object, reset.

A reset that raises means the object cannot be trusted: the pool destroys it (its disposal methods run, its place is freed) and the reset's error propagates from the release. The next meld builds a fresh object.

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/45_managed_spell_reset_methods.py
py -3.14t UX_and_AIX_experiences/02_intermediate/45_managed_spell_reset_methods.py

Download this collection ยท Source on GitHub

Public surface๏ƒ

md.Spellbook.bind (managed_spell_reset_methods, disposal_method_names), md.Existence.managed_spell_quantity, md.Conduit.meld, md.ReleaseManagedSpell (release, describe), md.Conduit.cleanup

Code๏ƒ

  1"""
  2TIER: intermediate (45)
  3GOAL: Hand the next caller a clean object - managed_spell_reset_methods.
  4      A pooled object is reused, so whatever the last holder left in it would leak to
  5      the next one. Name its reset methods at bind: zero-argument methods the pool
  6      calls, in the order given, on EVERY release, before the object is idle again.
  7      The next lease then gets the same object, reset.
  8
  9      A reset that raises means the object cannot be trusted: the pool destroys it
 10      (its disposal methods run, its place is freed) and the reset's error propagates
 11      from the release. The next meld builds a fresh object.
 12SURFACE EXERCISED: md.Spellbook.bind (managed_spell_reset_methods,
 13                   disposal_method_names), md.Existence.managed_spell_quantity,
 14                   md.Conduit.meld, md.ReleaseManagedSpell (release, describe),
 15                   md.Conduit.cleanup
 16"""
 17from typing import List, Tuple
 18
 19import melder as md
 20
 21
 22class ReportBuilder:
 23    """Accumulates report lines for one request; must start empty for the next one.
 24
 25    `clear_lines` and `rewind` are its reset methods; `calls` records the order the
 26    pool ran them, as a lesson diagnostic.
 27    """
 28
 29    def __init__(self) -> None:
 30        """Start with no lines and the cursor at the top."""
 31        self.lines: List[str] = []
 32        self.cursor: int = 0
 33        self.calls: List[str] = []
 34
 35    def clear_lines(self) -> None:
 36        """Drop the previous request's lines."""
 37        self.lines.clear()
 38        self.calls.append("clear_lines")
 39
 40    def rewind(self) -> None:
 41        """Move the cursor back to the top."""
 42        self.cursor = 0
 43        self.calls.append("rewind")
 44
 45
 46class TemplateCache:
 47    """A pooled cache whose reset refuses once the cache is corrupted.
 48
 49    `close` is its disposal method; the pool runs it when it destroys the object.
 50    """
 51
 52    def __init__(self) -> None:
 53        """Start valid and open."""
 54        self.corrupted: bool = False
 55        self.closed: bool = False
 56
 57    def reset(self) -> None:
 58        """Empty the cache for the next holder; a corrupted cache cannot be reset."""
 59        if self.corrupted:
 60            raise ValueError("template cache is corrupted and cannot be reset")
 61
 62    def close(self) -> None:
 63        """Dispose the cache; called when the pool destroys this object."""
 64        self.closed = True
 65
 66
 67def pool_counts(cls: type) -> Tuple[int, int, int]:
 68    """Return (alive, idle, out) for the one pool of a managed class."""
 69    (snapshot,) = md.ReleaseManagedSpell().describe(cls)
 70    return snapshot["count"], snapshot["idle_count"], snapshot["out_count"]
 71
 72
 73def main() -> None:
 74    """Reset a pooled object on release, then watch a failing reset destroy one.
 75
 76    Contract:
 77        Checks the reset order, the reused object's clean state, and that a raising
 78        reset disposes the object, frees its place and propagates its error.
 79    Returns:
 80        None.
 81    """
 82    book = md.Spellbook()
 83    try:
 84        book.bind(
 85            spell=ReportBuilder,
 86            existence=md.Existence.managed_spell_quantity,
 87            managed_spell_reset_methods=["clear_lines", "rewind"],
 88        )
 89        book.bind(
 90            spell=TemplateCache,
 91            existence=md.Existence.managed_spell_quantity,
 92            managed_spell_reset_methods=["reset"],
 93            disposal_method_names=["close"],
 94        )
 95        conduit = book.conjure()
 96        release = md.ReleaseManagedSpell().release
 97        try:
 98            builder: ReportBuilder = conduit.meld("ReportBuilder")
 99            builder.lines.extend(["header", "totals"])
100            builder.cursor = 2
101            release(builder)
102            # The resets ran in the order named at bind, on the release itself.
103            assert builder.calls == ["clear_lines", "rewind"]
104            assert builder.lines == [] and builder.cursor == 0
105            print("release ran the resets in order:", builder.calls)
106
107            nxt: ReportBuilder = conduit.meld("ReportBuilder")
108            assert nxt is builder and nxt.lines == []
109            print("the next request got the same builder, clean:", nxt is builder)
110            release(nxt)
111
112            cache: TemplateCache = conduit.meld("TemplateCache")
113            cache.corrupted = True
114            try:
115                release(cache)
116                raise AssertionError("a corrupted cache went back into the pool")
117            except ValueError as error:
118                # The pool destroyed the object: disposal ran and its place was freed.
119                assert cache.closed
120                assert pool_counts(TemplateCache) == (0, 0, 0)
121                print("a failing reset destroyed the cache and raised:", error)
122
123            fresh: TemplateCache = conduit.meld("TemplateCache")
124            assert fresh is not cache and not fresh.corrupted
125            assert pool_counts(TemplateCache) == (1, 0, 1)
126            print("the next meld built a fresh cache:", fresh is not cache)
127            release(fresh)
128        finally:
129            conduit.cleanup()
130    finally:
131        book.cleanup()
132
133
134if __name__ == "__main__":
135    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๏ƒ