On this page
Managed spell limited quantity๏
๐ก Intermediate ยท Lesson 43
Cap how many objects exist at once - managed_spell_mode="limited_quantity". managed_spell_limit (required here) is the most objects ALIVE at once: idle plus out. Below the limit a meld builds; at the limit, with nothing idle, the meld is refused with ManagedSpellExhaustedError before anything is built. The error is a MeldExecutionError that names the mode and the limit, and says how to get out: release an object, or bind a higher limit.
Releasing frees a place: the next meld is served the released object, so the pool never grows past its limit. release_all(objects) gives many back at once.
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/43_managed_spell_limited_quantity.py
py -3.14t UX_and_AIX_experiences/02_intermediate/43_managed_spell_limited_quantity.py
Public surface๏
md.Spellbook.bind (managed_spell_mode, managed_spell_limit), md.ManagedSpellMode.limited_quantity, md.Conduit.meld, md.ManagedSpellExhaustedError (mode, limit, timeout_seconds), md.MeldExecutionError, md.ReleaseManagedSpell (release, release_all, describe), md.Conduit.cleanup
Code๏
1"""
2TIER: intermediate (43)
3GOAL: Cap how many objects exist at once - managed_spell_mode="limited_quantity".
4 managed_spell_limit (required here) is the most objects ALIVE at once: idle plus
5 out. Below the limit a meld builds; at the limit, with nothing idle, the meld is
6 refused with ManagedSpellExhaustedError before anything is built. The error is a
7 MeldExecutionError that names the mode and the limit, and says how to get out:
8 release an object, or bind a higher limit.
9
10 Releasing frees a place: the next meld is served the released object, so the
11 pool never grows past its limit. release_all(objects) gives many back at once.
12SURFACE EXERCISED: md.Spellbook.bind (managed_spell_mode, managed_spell_limit),
13 md.ManagedSpellMode.limited_quantity, md.Conduit.meld,
14 md.ManagedSpellExhaustedError (mode, limit, timeout_seconds),
15 md.MeldExecutionError, md.ReleaseManagedSpell (release,
16 release_all, describe), md.Conduit.cleanup
17"""
18from typing import Tuple
19
20import melder as md
21
22
23class LicenseSeat:
24 """One seat of a licence that allows a fixed number of concurrent users.
25
26 `close` is its disposal method, run when the pool destroys the seat (root
27 teardown here). `closed` is a lesson diagnostic.
28 """
29
30 def __init__(self) -> None:
31 """Claim the seat; the lesson only records that it is open."""
32 self.closed: bool = False
33
34 def close(self) -> None:
35 """Give the seat up for good; called by the pool when it destroys this object."""
36 self.closed = True
37
38
39def pool_counts(cls: type) -> Tuple[int, int, int]:
40 """Return (alive, idle, out) for the one pool of a managed class."""
41 (snapshot,) = md.ReleaseManagedSpell().describe(cls)
42 return snapshot["count"], snapshot["idle_count"], snapshot["out_count"]
43
44
45def main() -> None:
46 """Fill a limited pool, read its refusal, then release and lease again.
47
48 Contract:
49 The refusal is caught and its fields checked; the counts prove the refusal
50 built nothing and that releases never let the pool grow past its limit.
51 Returns:
52 None.
53 """
54 book = md.Spellbook()
55 try:
56 book.bind(
57 spell=LicenseSeat,
58 existence=md.Existence.managed_spell_quantity,
59 managed_spell_mode="limited_quantity",
60 managed_spell_limit=2,
61 disposal_method_names=["close"],
62 )
63 conduit = book.conjure()
64 try:
65 first: LicenseSeat = conduit.meld("LicenseSeat")
66 second: LicenseSeat = conduit.meld("LicenseSeat")
67 assert pool_counts(LicenseSeat) == (2, 0, 2)
68 print("both seats taken (alive, idle, out):", pool_counts(LicenseSeat))
69
70 # At the limit with nothing idle: refused before anything is built.
71 try:
72 conduit.meld("LicenseSeat")
73 raise AssertionError("a third seat was leased past the limit")
74 except md.ManagedSpellExhaustedError as refused:
75 assert isinstance(refused, md.MeldExecutionError)
76 assert refused.mode == "limited_quantity" and refused.limit == 2
77 assert refused.timeout_seconds is None
78 assert "Release one with ReleaseManagedSpell().release(obj)" in str(refused)
79 print("third seat refused:", type(refused).__name__, "- mode", refused.mode, "limit", refused.limit)
80 assert pool_counts(LicenseSeat) == (2, 0, 2)
81
82 # A release frees a place, and the next meld is served the released seat.
83 md.ReleaseManagedSpell().release(first)
84 again: LicenseSeat = conduit.meld("LicenseSeat")
85 assert again is first
86 assert pool_counts(LicenseSeat) == (2, 0, 2)
87 print("after a release the next user got the freed seat:", again is first)
88
89 # release_all gives every seat back; the pool still holds exactly two.
90 md.ReleaseManagedSpell().release_all([second, again])
91 assert pool_counts(LicenseSeat) == (2, 2, 0)
92 reused = {id(conduit.meld("LicenseSeat")), id(conduit.meld("LicenseSeat"))}
93 assert reused == {id(first), id(second)}
94 assert pool_counts(LicenseSeat) == (2, 0, 2)
95 print("two more leases reused the same two seats; alive stays at the limit:", pool_counts(LicenseSeat))
96 finally:
97 conduit.cleanup()
98
99 # Teardown disposes only what is idle; both seats were still out.
100 assert not first.closed and not second.closed
101 print("seats still out at teardown stay their holder's:", not first.closed)
102 finally:
103 book.cleanup()
104
105
106if __name__ == "__main__":
107 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.