On this page
Spell spaces, lineage, and scopes that end๏
Prerequisite: Beginner scopes. Keep the scope boundary explicit: a child conduit, a conduit lineage, and a spell space answer different questions about where instances are shared.
Lifetime |
Scope demonstrated by the lessons |
|---|---|
|
A root and its child resolve different instances |
|
Root, child, and grandchild share one family instance |
|
Reuse inside one spell space; separate instances across spaces |
A job owns its child scope๏
Create a lesser conduit for a job, resolve the job's objects through it, and call
the child's cleanup() when the job ends. The scoped-cleanup lesson compares the
root and job sessions, ends the child, and resolves through the root again.
The root has a longer lifetime and is cleaned up separately when the application ends.
The runtime retains the objects it manages. Dropping your last local variable does not end that ownership; cleanup and memory ownership explains why the explicit end of the scope matters.
A lesser conduit can grow into a named root๏
A lesser can also have its own discovery name without being promoted. Run Named lesser conduits for a complete example with prewarming, collisions, per-conduit state and cleanup.
job = root.create_lesser_conduit(name="job-42")
cloud = root.get_conduit_cloud()
try:
assert cloud.get_conduit_by_name("job-42") is job
job.meld("JobBuffer")
finally:
job.cleanup()
assert not cloud.has_conduit_name("job-42")
Supply a name only at creation. Names are exact, nonempty strings, unique among active named roots and lessers in the frame. There is no later lesser rename. Cleanup unregisters and clears the name before the scope enters the pool; the next acquisition supplies its own name or stays anonymous. Anonymous scopes do not enter Cloud discovery. This works in automatic and dynamic mode.
Use a name when another part of the application needs to find the same live
scope. A direct reference is enough when the job stays local. Cloud returns a
borrowed reference: it does not extend the owner's lifetime or make reuse after
cleanup safe. Names are discovery addresses, not matching-lifetime tags; the
registered Existence still decides where objects are shared.
Aether reaches the same directory from the runtime root:
Aether().get_conduit_by_name("job-42") returns the named scope in the default
frame (pass aetheric_frame_name for another frame), and
Aether().get_conduit_by_id(job.id) finds any live scope by id, named or
anonymous. The root-only lookups carry root names, such as
get_root_conduit_by_name.
Frames have lookups of their own that never create anything (0.2.8208):
Aether().find_frame("ops") returns the live frame or None,
Aether().get_frame("ops") raises ValueError when it is missing, and
Aether().list_frame_names() lists the live frames in the order they were
created. The frame-scoped calls above create the default frame when it is
missing; these never do, not even for "default". A frame you find is borrowed,
so its owner can still clean it. For checks before you hand a configuration
over: frame.shared_spellbook_configuration is the configuration Spellbooks in
that frame adopt when the frame shares one, job.spellbook is the Spellbook a
conduit resolves through, SpellbookConfiguration.aether_frame and frozen
report a configuration's frame and whether it is settled, and
AethericFrameConfiguration.frozen does the same for a frame's posture. None
of them changes anything.
Promote only when independent ownership is needed๏
In a dynamic world, upgrade_to_normal(name=...) promotes the existing child.
The promotion lesson asserts that a previously created per-conduit workbench is
the same object afterward, then finds the promoted conduit through cloud lookup.
Read dynamic mode before using this operation.
Promotion can retain the lesser name or choose another available name. It keeps
the conduit ID but creates its own empty Spellbook; naming alone does none of that.
Clusters add a group-wide lifetime. The original cluster lesson remains in its saved Intermediate collection and is linked from that guide.
Purge creations while keeping the scope๏
Run Purge unneeded objects for a complete example. It releases one finished job buffer, keeps its siblings alive, purges the remaining buffers by name, and creates a fresh buffer afterward. It also checks that spell-space purge stays local and final cleanup does not dispose already-purged objects twice.
Use purge to dispose and remove the retained creations for one registered target
without ending its scope or removing its registration:
removed = conduit.purge("Job")
removed = space.purge("RequestSession")
Names, class/function references, spellframe/binding selectors, and explicit
spell_id= work through the same discovery as meld. The return value is the
number removed, or 0 when that authorized store has no matching creation.
purge_all=True is the default: a many target removes its whole retained bucket.
Only many objects with configured disposal are retained; untracked results are
not affected by purge.
An existing object provides a second input path: purge inspects its class and passes that reference through the existing binding lookup. Choose the shortcut or the explicit selectors for your binding. Both use the same scoped purge:
job = conduit.meld("Job")
removed = conduit.purge(job, purge_all=False) # Remove this object only.
removed = conduit.purge(job) # Remove remaining entries for its binding.
Single-object removal requires the instance and returns 0 if that object is
not retained in the authorized store. Other instances remain available for later
purge or normal scope cleanup. The same options work on SpellSpace.
Lifetime |
Who may purge it |
|---|---|
|
The conduit or spell space holding those local creations |
|
That spell space only |
|
That conduit only |
|
The lineage root conduit |
|
The elected cluster leader |
|
The spell's owning conduit |
|
No one: a conduit's purge returns |
A spell space always stays within its own creations. It cannot purge objects belonging to its conduit, lineage root, or cluster.
A managed spell's pool is not purged. Give its objects back with
ReleaseManagedSpell().release(obj); the pool disposes idle objects when it
trims them and when the root that owns it is torn down.
Managed spell pools walks through the whole lifecycle.
Purge follows the configured disposal methods and their ordering. It attempts
other selected objects after an object's disposal fails, then raises an
ExceptionGroup; removed entries remain removed. A subsequent meld can create a
fresh factory-backed instance using the existing compiled context. References
already held by application objects are not rewritten. An externally supplied
object remains referenced by its registration, so purging its store entry does
not unbind it or turn it into a factory.
A many dependency with configured disposal belongs to the conduit or spell
space that built it, not to the object it was passed to. Purging that object
runs only the object's own disposal methods; closing what it holds is up to
them. The dependency stays in the store that built it: that scope's end disposes
it, and purging the dependency's own binding there removes every instance the
store holds, including one a live object still uses.
The workflow in code๏
These are the core steps from the saved example. Use its complete linked script for all class definitions and setup.
1def main() -> None:
2 book = md.Spellbook()
3 book.bind(spell=JobSession, existence="unique_per_conduit",
4 disposal_method_names=["close"])
5 root = book.conjure()
6
7 # The long-lived scope holds its own session...
8 root_session = root.meld("JobSession")
9
10 # ...and each job gets a THROWAWAY scope with its own.
11 job = root.create_lesser_conduit()
12 job_session = job.meld("JobSession")
13 assert job_session is not root_session # per-conduit = per-scope
14
15 # Job done: end the SCOPE, not the world.
16 job.cleanup()
17 print("job scope closed its session:", job_session.closed)
18 print("root session untouched:", root_session.closed is False)
19
20 # The root never noticed - it keeps resolving.
21 assert root.meld("JobSession") is root_session
22 print("root still resolves after the child scope ended")
23
24 # And when the whole world is done, the same verb one level up.
25 root.cleanup()
26 print("root cleanup closed its session too:", root_session.closed)
Runnable examples๏
Scoped cleanup lesser conduits โ Intermediate 29
Named lesser conduits โ Intermediate 41
Spellspace scoped resolution โ Intermediate 04
Existence unique per conduit lineage โ Intermediate 05
Lesser conduits child scopes โ Intermediate 07
Upgrade to normal โ Intermediate 28
Purge unneeded objects โ Intermediate 39
All intermediate examples ยท Level contents ยท Full contents