On this page

The workstation canvas๏ƒ

๐ŸŸ  Advanced ยท Lesson 12

THE WORKSTATION - the room's binding canvas, and the last fixture in arc B. Every RiftSpace owns one, whatever its kind.

WHAT IT IS: a room-local scratchpad that holds things across steps. WHAT IT IS NOT, and this is the contract that matters:

"Stores only room-local bindings; it does not discover or resolve new targets from Melder/Nexus."

The workstation is NOT a resolver, NOT a registry, and NOT a second spellbook. It holds what you hand it. If you want something out of melder you get it through the command system (lesson 11) and then park it here. Keeping those two jobs apart is why the canvas can be wiped without touching the world.

THREE LOGICAL STORES, kept separate on purpose: objects bind_object(name, value) attributes bind_attribute(name, value) methods bind_method(name, value) Same name can live in two stores without collision, because get and release take a store= selector.

ONE ACTIVE TARGET AT A TIME set_target(name, store=...) select a saved binding as THE target get_target() read it back call_target(*args) invoke it (optionally re-binding the result straight back onto the canvas) clear_target() deselect without deleting the binding "At most one active target" is a deliberate ceiling. A canvas with many simultaneous targets is a canvas where "the target" stops meaning anything.

WEAK BY REQUEST, AND NEVER BY ACCIDENT weak_ref=True force weak storage weak_ref=False force strong storage weak_ref=None use the room-local default captured at creation And the rule worth carrying to the rest of the library:

"Explicit weak binding RAISES when the supplied value cannot be weak-referenced; IT NEVER SILENTLY DEGRADES TO STRONG STORAGE."

That is the same honesty you met at validate() in lesson 06, which raises instead of returning False. Melder would rather fail loudly than quietly give you something adjacent to what you asked for. A silent degrade here would mean an object you believed was collectable is pinned for the life of the room - a leak that looks like correct code.

Before you run๏ƒ

Use the Advanced 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/03_advanced/12_the_workstation_canvas.py
py -3.14t UX_and_AIX_experiences/03_advanced/12_the_workstation_canvas.py

Download this collection ยท Source on GitHub

Public surface๏ƒ

md.Workstation via room.workstation - bind_object, bind_method, get, release, describe_bindings, set_target/get_target/clear_target, weak_ref semantics

Code๏ƒ

  1"""
  2TIER: advanced (12)
  3GOAL: THE WORKSTATION - the room's binding canvas, and the last fixture
  4      in arc B. Every RiftSpace owns one, whatever its kind.
  5
  6      WHAT IT IS: a room-local scratchpad that holds things across steps.
  7      WHAT IT IS NOT, and this is the contract that matters:
  8
  9        "Stores only room-local bindings; it does not discover or resolve
 10         new targets from Melder/Nexus."
 11
 12      The workstation is NOT a resolver, NOT a registry, and NOT a second
 13      spellbook. It holds what you hand it. If you want something out of
 14      melder you get it through the command system (lesson 11) and then
 15      park it here. Keeping those two jobs apart is why the canvas can be
 16      wiped without touching the world.
 17
 18      THREE LOGICAL STORES, kept separate on purpose:
 19        objects     bind_object(name, value)
 20        attributes  bind_attribute(name, value)
 21        methods     bind_method(name, value)
 22      Same name can live in two stores without collision, because `get`
 23      and `release` take a `store=` selector.
 24
 25      ONE ACTIVE TARGET AT A TIME
 26        set_target(name, store=...)  select a saved binding as THE target
 27        get_target()                 read it back
 28        call_target(*args)           invoke it (optionally re-binding the
 29                                     result straight back onto the canvas)
 30        clear_target()               deselect without deleting the binding
 31      "At most one active target" is a deliberate ceiling. A canvas with
 32      many simultaneous targets is a canvas where "the target" stops
 33      meaning anything.
 34
 35      WEAK BY REQUEST, AND NEVER BY ACCIDENT
 36        weak_ref=True   force weak storage
 37        weak_ref=False  force strong storage
 38        weak_ref=None   use the room-local default captured at creation
 39      And the rule worth carrying to the rest of the library:
 40
 41        "Explicit weak binding RAISES when the supplied value cannot be
 42         weak-referenced; IT NEVER SILENTLY DEGRADES TO STRONG STORAGE."
 43
 44      That is the same honesty you met at validate() in lesson 06, which
 45      raises instead of returning False. Melder would rather fail loudly
 46      than quietly give you something adjacent to what you asked for. A
 47      silent degrade here would mean an object you believed was
 48      collectable is pinned for the life of the room - a leak that looks
 49      like correct code.
 50SURFACE EXERCISED: md.Workstation via room.workstation - bind_object,
 51                   bind_method, get, release, describe_bindings,
 52                   set_target/get_target/clear_target, weak_ref semantics
 53VERIFY: rides the owner's 3.14t run; asserts are the contract.
 54
 55DOC DRIFT FOUND AND FIXED (2026-08-02): describe_bindings() used to
 56document "a FOUR-KEY summary - `objects`, `attributes`, `methods` and
 57`target_name` - always with all four keys present, so callers can index
 58them" while RETURNING FIVE; the implementation also emits `target_store`.
 59That one had teeth, because the docstring explicitly invited callers to
 60rely on the count. Now documented as five, with `target_store` explained:
 61it names WHICH store the active target came from, so a caller can
 62round-trip it back through get(name, store=...).
 63"""
 64import melder as md
 65
 66
 67class Greeter:
 68    """A weak-referenceable object - unlike an int."""
 69
 70    def greet(self) -> str:
 71        return "hello from the canvas"
 72
 73
 74def main() -> None:
 75    nexus = md.Nexus()
 76    system_config = nexus.create_configuration()
 77    system_config.with_rift_creation_enabled(True)
 78    nexus.activate(system_config)
 79
 80    rift_config = nexus.create_rift_configuration()
 81    rift_config.with_space_type("capability")
 82    rift = nexus.create_rift(configuration=rift_config, rift_name="bench")
 83    rift.mark_active()
 84
 85    workstation = rift.space.workstation
 86    assert isinstance(workstation, md.Workstation)
 87    assert workstation.owner_space_id == rift.space.space_id
 88    print("workstation:", workstation.workstation_id)
 89    print("owned by room:", workstation.owner_space_id == rift.space.space_id)
 90
 91    # THREE STORES. The same name in two stores is not a collision.
 92    greeter = Greeter()
 93    workstation.bind_object("subject", greeter)
 94    workstation.bind_method("subject", greeter.greet)
 95    print("bound 'subject' into two different stores")
 96
 97    from_objects = workstation.get("subject", store="objects")
 98    from_methods = workstation.get("subject", store="methods")
 99    assert from_objects is greeter
100    assert from_methods() == "hello from the canvas"
101    print("objects['subject'] is the instance; methods['subject'] is callable")
102
103    # THE READ DOOR. Ask the canvas what is on it.
104    summary = workstation.describe_bindings()
105    print("describe_bindings keys:", sorted(summary))
106    for store in ("objects", "attributes", "methods", "target_name"):
107        assert store in summary, store
108    assert "subject" in summary["objects"]
109    assert "subject" in summary["methods"]
110
111    # Five keys, and the docstring now says five. `target_store` names the
112    # store the active target came from - enough to round-trip it back
113    # through get(name, store=...).
114    assert set(summary) == {"objects", "attributes", "methods",
115                            "target_name", "target_store"}
116    print("keys:", len(summary), "- documented and returned agree")
117
118    # ONE TARGET AT A TIME.
119    workstation.set_target("subject", store="methods")
120    assert workstation.get_target() is not None
121    print("target set from the methods store")
122
123    result = workstation.call_target()
124    assert result == "hello from the canvas"
125    print("call_target ->", result)
126
127    workstation.clear_target()
128    # Clearing deselects; it does not delete the binding underneath.
129    assert workstation.get("subject", store="methods") is not None
130    print("target cleared; the binding it pointed at is still there")
131
132    # WEAK BY REQUEST. A class instance can be weak-referenced.
133    workstation.bind_object("weak_subject", Greeter(), weak_ref=True)
134    print("weak binding accepted for a weak-referenceable object")
135
136    # ...and an int cannot. This RAISES rather than quietly storing it
137    # strongly, which is the whole point.
138    try:
139        workstation.bind_object("weak_number", 42, weak_ref=True)
140        raise AssertionError(
141            "expected a refusal - int cannot be weak-referenced"
142        )
143    except (TypeError, ValueError, RuntimeError) as error:
144        print("explicit weak binding refused:", type(error).__name__)
145
146    # No silent degrade means: it is not on the canvas at all.
147    after = workstation.describe_bindings()
148    assert "weak_number" not in after["objects"]
149    print("refused binding was NOT stored strongly as a fallback")
150
151    # release() takes it back off the canvas and hands it to you.
152    released = workstation.release("subject", store="objects")
153    assert released is greeter
154    assert "subject" not in workstation.describe_bindings()["objects"]
155    print("released 'subject' from objects; methods copy untouched:",
156          "subject" in workstation.describe_bindings()["methods"])
157
158    print()
159    print("the canvas holds; the command system resolves. separate jobs.")
160    print("weak when asked, never by accident - refuse instead of degrade")
161
162
163if __name__ == "__main__":
164    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 advanced examples ยท Level guide

API contracts๏ƒ