Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.python.host

THE door from the sandbox back into Vis, and the only one.

The embedded interpreter calls out through ONE function it was handed, so everything a block can ask the host for arrives here as two strings: the name of a tool and a JSON envelope of its arguments. What crosses is DATA - a tool takes plain values and answers plain values, because the boundary carries text and nothing else. A live object (an interpreter handle, a stream, a file) crossed it and never will; a handle the model holds is a PYTHON object built over calls that come back through here.

One interpreter serves every session in this process, so the envelope names the session that called (vis-python-runtime's install) and this registry answers per session: shell bound for two workspaces is two different functions under one name, and a session that never bound a name gets a refusal that says so instead of a neighbour's tool.

The host is bound ONCE per process. Binding is idempotent because the interpreter is: a second bind! would replace a live upcall stub while a block sits inside it.

THE door from the sandbox back into Vis, and the only one.

The embedded interpreter calls out through ONE function it was handed, so
everything a block can ask the host for arrives here as two strings: the name
of a tool and a JSON envelope of its arguments. What crosses is DATA - a tool
takes plain values and answers plain values, because the boundary carries
text and nothing else. A live object (an interpreter handle, a stream, a file)
crossed it and never will; a handle the model holds is a PYTHON object built
over calls that come back through here.

One interpreter serves every session in this process, so the envelope names
the session that called (`vis-python-runtime`'s `install`) and this registry
answers per session: `shell` bound for two workspaces is two different
functions under one name, and a session that never bound a name gets a
refusal that says so instead of a neighbour's tool.

The host is bound ONCE per process. Binding is idempotent because the
interpreter is: a second `bind!` would replace a live upcall stub while a
block sits inside it.
raw docstring

bind!clj

(bind!)

Make dispatch the host THIS process's interpreter calls back into, if this process has an interpreter to bind.

Idempotent: the interpreter holds one upcall stub, and rebinding it while a block is inside a call would swap the target under a live frame. Answers whether a host is bound now.

Two things this used to get wrong, both measured while loading a Python extension in a process that never started a sandbox. It marked itself done BEFORE the bind, so a bind that threw left the flag set and every later caller skipped it — the first extension in such a process failed to load and the rest only appeared to work. And it insisted on the interpreter existing at all: binding needs the cdylib resolved, which a process that only talks to the extension host has no reason to have fetched. A library that will not resolve is not an error here, it is a process with nothing to bind — the extension host binds its OWN interpreter in its own process, and the parent binds when it builds a sandbox.

Make [[dispatch]] the host THIS process's interpreter calls back into, if this
process has an interpreter to bind.

Idempotent: the interpreter holds one upcall stub, and rebinding it while a
block is inside a call would swap the target under a live frame. Answers
whether a host is bound now.

Two things this used to get wrong, both measured while loading a Python
extension in a process that never started a sandbox. It marked itself done
BEFORE the bind, so a bind that threw left the flag set and every later caller
skipped it — the first extension in such a process failed to load and the rest
only appeared to work. And it insisted on the interpreter existing at all:
binding needs the cdylib resolved, which a process that only talks to the
extension host has no reason to have fetched. A library that will not resolve
is not an error here, it is a process with nothing to bind — the extension
host binds its OWN interpreter in its own process, and the parent binds when
it builds a sandbox.
sourceraw docstring

conveyingcljmacro

(conveying session & body)

Evaluate body, conveying the current dynamic bindings to session's host calls.

Evaluate `body`, conveying the current dynamic bindings to `session`'s host calls.
sourceraw docstring

conveying*clj

(conveying* session f)

Call f, conveying THIS thread's dynamic bindings to session's host calls.

Call `f`, conveying THIS thread's dynamic bindings to `session`'s host calls.
sourceraw docstring

dispatchclj

(dispatch caller tool payload)

Serve one call from the sandbox: WHO called, payload in, reply JSON out.

caller is the INTERPRETER's answer — the namespace the call was made from — and it is the only thing this authorizes against. The payload also carries a session, because the guest's envelope always did, but that field is written by the guest: a block that named a neighbour's session used to be served the neighbour's tools, with the neighbour's roots (measured: a confined block read a file its own policy had refused it one statement earlier). A mismatch is a forgery attempt and is recorded as one.

THE host function itself, kept public so a test can measure the boundary without an interpreter. It never throws: a failure is a reply the guest raises as RuntimeError, because a host that throws here unwinds through the interpreter's upcall stub.

Serve one call from the sandbox: WHO called, `payload` in, reply JSON out.

`caller` is the INTERPRETER's answer — the namespace the call was made from —
and it is the only thing this authorizes against. The payload also carries a
session, because the guest's envelope always did, but that field is written
by the guest: a block that named a neighbour's session used to be served the
neighbour's tools, with the neighbour's roots (measured: a confined block read
a file its own policy had refused it one statement earlier). A mismatch is a
forgery attempt and is recorded as one.

THE host function itself, kept public so a test can measure the boundary
without an interpreter. It never throws: a failure is a reply the guest
raises as `RuntimeError`, because a host that throws here unwinds through the
interpreter's upcall stub.
sourceraw docstring

forget-session!clj

(forget-session! session)

Drop session's bindings, answering how many names went.

The interpreter's namespace is closed by whoever owns the session; this is the host half, and leaving it behind would keep every closure a finished session captured.

Drop `session`'s bindings, answering how many names went.

The interpreter's namespace is closed by whoever owns the session; this is
the host half, and leaving it behind would keep every closure a finished
session captured.
sourceraw docstring

forget-tools!clj

(forget-tools! session names)

Drop names from session's registry, answering nothing.

For a tool that exists only for the length of ONE call — a host callback an adapter hands INTO Python — the registry is what makes the name callable, so forgetting it is what takes the capability back.

Drop `names` from `session`'s registry, answering nothing.

For a tool that exists only for the length of ONE call — a host callback an
adapter hands INTO Python — the registry is what makes the name callable, so
forgetting it is what takes the capability back.
sourceraw docstring

install-doors!clj

(install-doors! session tools)
(install-doors! session tools install-one)

Install tools into session AND register them for every session.

A shim door is ONE host capability, not a per-session closure, and Python is PROCESS state: attach and ls staple their entry points onto builtins, so a second session can find names the FIRST session built — holding proxies that still name it. The shared key is what keeps those doors answering after that session is gone.

Install `tools` into `session` AND register them for every session.

A shim door is ONE host capability, not a per-session closure, and Python is
PROCESS state: `attach` and `ls` staple their entry points onto `builtins`, so
a second session can find names the FIRST session built — holding proxies that
still name it. The shared key is what keeps those doors answering after that
session is gone.
sourceraw docstring

install-sync-tools!clj

(install-sync-tools! session tools)
(install-sync-tools! session tools install-one)

Bind tools as session's host tools that answer DIRECTLY, and answer the names bound.

For Python the host runs, not the model: a sandbox tool hands back a thunk the block runner settles, and trusted code - an extension calling vis.shell(...) in the middle of a line - has no runner and no await. The installer is a PARAMETER because trusted Python does not run where the sandbox does: an extension session binds its names in the unconfined child process, a sandbox session in this one, and the registry that answers them is the same either way.

Bind `tools` as `session`'s host tools that answer DIRECTLY, and answer the
names bound.

For Python the host runs, not the model: a sandbox tool hands back a thunk
the block runner settles, and trusted code - an extension calling
`vis.shell(...)` in the middle of a line - has no runner and no `await`.
The installer is a PARAMETER because trusted Python does not run where the
sandbox does: an extension session binds its names in the unconfined child
process, a sandbox session in this one, and the registry that answers them is
the same either way.
sourceraw docstring

install-tools!clj

(install-tools! session tools)
(install-tools! session tools bind-one)

Bind tools - {python-name fn} - as session's host tools and answer the names bound.

Registration and installation are ONE step on purpose: a name the guest can call but the registry does not know is a refusal the model cannot act on.

bind-one says WHERE the guest half is bound: this process's interpreter by default, or a session's own worker when the caller hands one in. The registry half is the parent's either way — a worker calls back here.

Bind `tools` - `{python-name fn}` - as `session`'s host tools and answer the
names bound.

Registration and installation are ONE step on purpose: a name the guest can
call but the registry does not know is a refusal the model cannot act on.

`bind-one` says WHERE the guest half is bound: this process's interpreter by
default, or a session's own worker when the caller hands one in. The registry
half is the parent's either way — a worker calls back here.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close