On this page

Upgrade to normal๏ƒ

๐ŸŸก Intermediate ยท Lesson 28

upgrade_to_normal - a lesser conduit GROWS UP in place. Lessers are unnamed child scopes (lesson 07); in a dynamic world one can be promoted to a full named citizen: registered in the world, discoverable by name in the cloud, able to do everything a normal conduit does. The promotion retains what the child already built for disposal, but owns a NEW EMPTY Spellbook: former definitions are no longer visible. Bind the new root's definitions explicitly. Dynamic-only verb: in a static world the same call refuses.

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

Download this collection ยท Source on GitHub

Public surface๏ƒ

create_lesser_conduit, upgrade_to_normal, cloud name lookup after promotion

Code๏ƒ

 1"""
 2TIER: intermediate (28)
 3GOAL: upgrade_to_normal - a lesser conduit GROWS UP in place. Lessers
 4      are unnamed child scopes (lesson 07); in a dynamic world one can
 5      be promoted to a full named citizen: registered in the world,
 6      discoverable by name in the cloud, able to do everything a normal
 7      conduit does. The promotion retains what the child already built
 8      for disposal, but owns a NEW EMPTY Spellbook: former definitions
 9      are no longer visible. Bind the new root's definitions explicitly.
10      Dynamic-only verb: in a static world the same call refuses.
11SURFACE EXERCISED: create_lesser_conduit, upgrade_to_normal,
12                   cloud name lookup after promotion
13"""
14import sys
15from pathlib import Path
16
17sys.path.insert(0, str(Path(__file__).parent))  # local helper (see _dynamic_world)
18from _dynamic_world import dynamic_spellbook
19
20import melder as md
21
22
23class Workbench:
24    """Track disposal of an object created before promotion."""
25
26    def __init__(self) -> None:
27        """Start with no cleanup calls so promotion can prove retention."""
28        self.cleanup_calls = 0
29
30    def cleanup(self) -> None:
31        """Record disposal by the scope retaining this creation."""
32        self.cleanup_calls += 1
33
34
35class IndependentWorkbench:
36    """A definition owned only by the newly graduated root's Book."""
37
38
39def main() -> None:
40    """Promote in place, register independent definitions and prove disposal ownership."""
41    book = dynamic_spellbook()
42    book.bind(spell=Workbench, existence="unique_per_conduit", disposal_method_names=["cleanup"])
43    root = book.conjure(dynamic=True, name="factory-floor")   # settles
44
45    # An unnamed child scope, working away...
46    worker = root.create_lesser_conduit()
47    bench_before = worker.meld("Workbench")
48
49    try:
50        # ...promoted in place. Its new empty Book is already conjured.
51        worker.upgrade_to_normal(name="worker")
52
53        # It KEPT its object for disposal, without inheriting the old definition.
54        assert bench_before.cleanup_calls == 0
55        try:
56            worker.meld("Workbench")
57        except KeyError:
58            print("new Book has no former Workbench definition")
59        else:
60            raise AssertionError("Graduation must start with an empty Book.")
61        worker.bind(spell=IndependentWorkbench, existence="unique_per_conduit")
62        assert isinstance(worker.meld("IndependentWorkbench"), IndependentWorkbench)
63        print("new root can bind and meld its own definitions")
64
65        # And it is now a discoverable citizen of the world.
66        cloud = root.get_conduit_cloud()
67        assert cloud.get_conduit_by_name("worker") is worker
68        print("promoted conduit found by name:", worker.name)
69        worker.cleanup()
70        assert bench_before.cleanup_calls == 1
71        assert isinstance(root.meld("Workbench"), Workbench)
72        print("new root disposed its retained bench; former root still works")
73    finally:
74        worker.cleanup()
75        root.cleanup()
76        book.cleanup()
77
78
79if __name__ == "__main__":
80    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