beatrina 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +92 -0
- package/NOTICES +72 -0
- package/README.md +124 -0
- package/bin/beatrina.mjs +223 -0
- package/bin/cli.mjs +80 -0
- package/bin/failsafe.mjs +74 -0
- package/bin/identity.mjs +45 -0
- package/bin/prova-post.mjs +51 -0
- package/bin/sessions.mjs +95 -0
- package/bin/shortcut.mjs +151 -0
- package/bin/update-check.mjs +55 -0
- package/bin/upgrade.mjs +76 -0
- package/build-info.json +1 -0
- package/carmar_V0.8.6.html +1310 -0
- package/check/acceptance.mjs +278 -0
- package/check/session.mjs +215 -0
- package/engines/js/document-exec.mjs +82 -0
- package/engines/js/persist.mjs +214 -0
- package/engines/js/worker.mjs +424 -0
- package/engines/python/adapter.py +577 -0
- package/engines/python/analyze.py +814 -0
- package/engines/python/bootstrap.py +309 -0
- package/engines/python/dataview.py +735 -0
- package/engines/python/debugger.py +346 -0
- package/engines/python/document_exec.py +158 -0
- package/engines/python/engine.json +28 -0
- package/engines/python/handoff.py +118 -0
- package/engines/python/worker.py +564 -0
- package/engines/r/engine.json +25 -0
- package/engines/r/handoff.R +92 -0
- package/failsafe/ai-policy.R +255 -0
- package/failsafe/ai-store.R +373 -0
- package/failsafe/cite.R +418 -0
- package/failsafe/journal.R +684 -0
- package/failsafe/plugins.R +809 -0
- package/failsafe/serve.R +5500 -0
- package/host/ai-policy.mjs +218 -0
- package/host/deployment.mjs +160 -0
- package/host/engine-js.mjs +98 -0
- package/host/engine-pool.mjs +383 -0
- package/host/engine-python.mjs +228 -0
- package/host/engine-r.mjs +206 -0
- package/host/engine-stdio.mjs +401 -0
- package/host/journal-store.mjs +749 -0
- package/host/main.mjs +503 -0
- package/host/planes/README.md +41 -0
- package/host/planes/ai-store.mjs +327 -0
- package/host/planes/ai.mjs +467 -0
- package/host/planes/analyze.mjs +397 -0
- package/host/planes/cite.mjs +517 -0
- package/host/planes/files.mjs +0 -0
- package/host/planes/jobs.mjs +704 -0
- package/host/planes/journal.mjs +53 -0
- package/host/planes/latex.mjs +201 -0
- package/host/planes/mcp.mjs +493 -0
- package/host/planes/pair.mjs +325 -0
- package/host/planes/pipe-term.mjs +122 -0
- package/host/planes/plugins.mjs +112 -0
- package/host/planes/proc-tree.mjs +76 -0
- package/host/planes/sessions.mjs +434 -0
- package/host/planes/settings.mjs +164 -0
- package/host/planes/terminal.mjs +286 -0
- package/host/planes/test-file.mjs +80 -0
- package/host/planes/update.mjs +214 -0
- package/host/plugin-store.mjs +838 -0
- package/host/server.mjs +441 -0
- package/host/settings.mjs +379 -0
- package/host/update-record.mjs +59 -0
- package/host/user-dirs.mjs +117 -0
- package/host/windows-runtime.mjs +145 -0
- package/host/worker-plane.mjs +713 -0
- package/host/ws.mjs +190 -0
- package/kernel/analyze.R +668 -0
- package/kernel/deployment.R +165 -0
- package/kernel/examples/NOTICE.md +38 -0
- package/kernel/examples/tna-complete-tutorial.Rmd +210 -0
- package/kernel/fileio.R +656 -0
- package/kernel/index.html +96 -0
- package/kernel/job-run.R +391 -0
- package/kernel/jobs.R +276 -0
- package/kernel/kernel-protocol +1 -0
- package/kernel/kernel-version +1 -0
- package/kernel/kernel.R +671 -0
- package/kernel/knitr-run.R +245 -0
- package/kernel/latex.R +609 -0
- package/kernel/mcp/carmar-mcp.mjs +516 -0
- package/kernel/notebook-page.R +67 -0
- package/kernel/plugins/csl/apa/apa.csl +2273 -0
- package/kernel/plugins/csl/apa/plugin.json +19 -0
- package/kernel/plugins/csl/chicago-author-date/chicago-author-date.csl +4216 -0
- package/kernel/plugins/csl/chicago-author-date/plugin.json +19 -0
- package/kernel/plugins/csl/harvard-cite-them-right/harvard-cite-them-right.csl +316 -0
- package/kernel/plugins/csl/harvard-cite-them-right/plugin.json +19 -0
- package/kernel/plugins/csl/ieee/ieee.csl +519 -0
- package/kernel/plugins/csl/ieee/plugin.json +19 -0
- package/kernel/plugins/csl/modern-language-association/modern-language-association.csl +1184 -0
- package/kernel/plugins/csl/modern-language-association/plugin.json +19 -0
- package/kernel/plugins/csl/nature/nature.csl +189 -0
- package/kernel/plugins/csl/nature/plugin.json +19 -0
- package/kernel/plugins/latex/apa7/apa7.json +14 -0
- package/kernel/plugins/latex/apa7/plugin.json +19 -0
- package/kernel/plugins/latex/elsarticle/elsarticle.json +14 -0
- package/kernel/plugins/latex/elsarticle/plugin.json +19 -0
- package/kernel/plugins/latex/ieeetran/ieeetran.json +10 -0
- package/kernel/plugins/latex/ieeetran/plugin.json +19 -0
- package/kernel/project.R +131 -0
- package/kernel/settings.R +410 -0
- package/kernel/sniff.R +769 -0
- package/kernel/worker-boot.R +22 -0
- package/kernel/worker.R +3496 -0
- package/lib/agent-authoring-contract.js +547 -0
- package/lib/cell-kinds.js +108 -0
- package/lib/engine-labels.js +324 -0
- package/package.json +32 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""bootstrap.py — code executed silently inside the ipykernel at startup.
|
|
2
|
+
|
|
3
|
+
Ported from `../carmar-python/py/carmapy/bootstrap.py` (read 2026-09-14), with
|
|
4
|
+
one addition: `__carmapy_reply` now also answers `env`, so the notebook's
|
|
5
|
+
Environment pane has something to show for a Python session.
|
|
6
|
+
|
|
7
|
+
This is how a pandas DataFrame becomes Beatrix's `dataframe` frame rather than
|
|
8
|
+
a blob of HTML: we register an IPython display formatter for a private MIME
|
|
9
|
+
type, so the ordinary displayhook — the thing Jupyter already does well — hands
|
|
10
|
+
the adapter a payload already in the frame's shape.
|
|
11
|
+
|
|
12
|
+
Borrowing the MIME-bundle displayhook instead of re-implementing a displayhook
|
|
13
|
+
is the entire reason the Python worker is 600 lines rather than 3,000.
|
|
14
|
+
|
|
15
|
+
Two rules copied from `emit_dataframe` in `spike/worker.R`, because they are
|
|
16
|
+
correctness, not polish:
|
|
17
|
+
|
|
18
|
+
* A returned value is a NOTEBOOK PREVIEW, not the data viewer. Bounding rows
|
|
19
|
+
alone is insufficient — one cell can hold a fitted model or megabytes of
|
|
20
|
+
text — so complex cells are flattened to a description and text is clipped,
|
|
21
|
+
then a final wire-size ceiling is enforced.
|
|
22
|
+
* The preview is 200 rows, because the page's own ROWS_PER_PAGE is 25 and a
|
|
23
|
+
smaller preview makes the row navigator unreachable.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import base64 as _base64
|
|
27
|
+
from pathlib import Path as _Path
|
|
28
|
+
|
|
29
|
+
CARMAR_MIME = "application/vnd.carmar.dataframe+json"
|
|
30
|
+
# The reply channel, and the kernel's own housekeeping, each on their own
|
|
31
|
+
# private MIME. Two, not one: a `view` reply is a REPLY the worker is waiting
|
|
32
|
+
# on by wire id, while the cwd is UNSOLICITED state the worker records and
|
|
33
|
+
# never forwards. Folding them together would make "is anyone waiting for
|
|
34
|
+
# this?" a field rather than a type.
|
|
35
|
+
REPLY_MIME = "application/vnd.carmapy.reply+json"
|
|
36
|
+
CONTROL_MIME = "application/vnd.carmapy.control+json"
|
|
37
|
+
|
|
38
|
+
# `dataview.py` is shipped into the kernel as SOURCE, base64-encoded.
|
|
39
|
+
#
|
|
40
|
+
# Source rather than an import, because the kernel may be any Python
|
|
41
|
+
# environment the user chose and Beatrix's engine package need not be installed
|
|
42
|
+
# in it. Base64 rather than an embedded string literal, because the bootstrap
|
|
43
|
+
# is Python source, so it eventually contains a quote, a backslash or a `%`,
|
|
44
|
+
# and one of those breaks the template the first time it appears.
|
|
45
|
+
_DATAVIEW_B64 = _base64.b64encode(
|
|
46
|
+
_Path(__file__).with_name("dataview.py").read_bytes()).decode()
|
|
47
|
+
# `debugger.py`, the same way and for the same reasons. Installed EAGERLY, not
|
|
48
|
+
# on first use: it names the next cell after its chunk (the breakpoint address),
|
|
49
|
+
# and a function defined before the first breakpoint was set must already carry
|
|
50
|
+
# that name, exactly as R parses every chunk under its srcname whether or not
|
|
51
|
+
# anyone is debugging.
|
|
52
|
+
_DEBUGGER_B64 = _base64.b64encode(
|
|
53
|
+
_Path(__file__).with_name("debugger.py").read_bytes()).decode()
|
|
54
|
+
# `handoff.py` (share/use), the same way: standard library only.
|
|
55
|
+
_HANDOFF_B64 = _base64.b64encode(
|
|
56
|
+
_Path(__file__).with_name("handoff.py").read_bytes()).decode()
|
|
57
|
+
|
|
58
|
+
BOOTSTRAP = r'''
|
|
59
|
+
def __carmar_install_formatters():
|
|
60
|
+
import json
|
|
61
|
+
|
|
62
|
+
PREVIEW_ROWS = 200
|
|
63
|
+
MAX_CELL_CHARS = 512
|
|
64
|
+
MAX_WIRE_BYTES = 512 * 1024
|
|
65
|
+
|
|
66
|
+
def _cell(v):
|
|
67
|
+
"""Flatten anything that is not a plain scalar to a description."""
|
|
68
|
+
if v is None:
|
|
69
|
+
return None
|
|
70
|
+
if isinstance(v, bool) or isinstance(v, int):
|
|
71
|
+
return v
|
|
72
|
+
if isinstance(v, float):
|
|
73
|
+
# NaN/Inf are not JSON; the page reads null the way R's na="null" does.
|
|
74
|
+
return v if v == v and v not in (float("inf"), float("-inf")) else None
|
|
75
|
+
if isinstance(v, str):
|
|
76
|
+
return v if len(v) <= MAX_CELL_CHARS else v[:MAX_CELL_CHARS] + "…"
|
|
77
|
+
try:
|
|
78
|
+
import numpy as np
|
|
79
|
+
if isinstance(v, np.generic):
|
|
80
|
+
return _cell(v.item())
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
s = repr(v)
|
|
84
|
+
if len(s) > MAX_CELL_CHARS:
|
|
85
|
+
s = s[:MAX_CELL_CHARS] + "…"
|
|
86
|
+
return s
|
|
87
|
+
|
|
88
|
+
def _frame(df):
|
|
89
|
+
total_rows, total_cols = int(df.shape[0]), int(df.shape[1])
|
|
90
|
+
head = df.head(PREVIEW_ROWS)
|
|
91
|
+
cols = [str(c) for c in head.columns]
|
|
92
|
+
types = [str(t) for t in head.dtypes]
|
|
93
|
+
rows = [{cols[i]: _cell(v) for i, v in enumerate(rec)}
|
|
94
|
+
for rec in head.itertuples(index=False, name=None)]
|
|
95
|
+
|
|
96
|
+
payload = {
|
|
97
|
+
"columns": cols,
|
|
98
|
+
"types": types,
|
|
99
|
+
"nrow": total_rows,
|
|
100
|
+
"ncol": total_cols,
|
|
101
|
+
"shown": len(rows),
|
|
102
|
+
"shownCols": len(cols),
|
|
103
|
+
"truncated": total_rows > len(rows) or total_cols > len(cols),
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Final wire ceiling. Halving is what R did and it is wrong from a big
|
|
107
|
+
# head (it costs a third of the rows that would have fit), so estimate
|
|
108
|
+
# the per-row cost and keep what fits, as view_payload does.
|
|
109
|
+
body = json.dumps({**payload, "rows": rows})
|
|
110
|
+
if len(body.encode()) > MAX_WIRE_BYTES and rows:
|
|
111
|
+
per = len(body.encode()) / max(1, len(rows))
|
|
112
|
+
keep = max(1, int(MAX_WIRE_BYTES // per))
|
|
113
|
+
rows = rows[:keep]
|
|
114
|
+
payload["shown"] = len(rows)
|
|
115
|
+
payload["truncated"] = True
|
|
116
|
+
payload["rows"] = rows
|
|
117
|
+
return json.dumps(payload)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
ip = get_ipython()
|
|
121
|
+
except NameError:
|
|
122
|
+
return
|
|
123
|
+
fmts = ip.display_formatter.formatters
|
|
124
|
+
if "__CARMAR_MIME__" not in fmts:
|
|
125
|
+
from IPython.core.formatters import BaseFormatter
|
|
126
|
+
from traitlets import Unicode, ObjectName
|
|
127
|
+
class _CarmarDF(BaseFormatter):
|
|
128
|
+
format_type = Unicode("__CARMAR_MIME__")
|
|
129
|
+
print_method = ObjectName("_repr_carmar_df_")
|
|
130
|
+
_return_type = str
|
|
131
|
+
fmts["__CARMAR_MIME__"] = _CarmarDF(parent=ip.display_formatter)
|
|
132
|
+
# pandas 3.0 sets DataFrame.__module__ = "pandas"; every older example in
|
|
133
|
+
# the ecosystem says "pandas.core.frame", which on pandas 3.x matches
|
|
134
|
+
# NOTHING and fails silently — the formatter registers, reports active, and
|
|
135
|
+
# is never called. Register both spellings; a deferred name that never
|
|
136
|
+
# resolves costs nothing.
|
|
137
|
+
for mod in ("pandas", "pandas.core.frame"):
|
|
138
|
+
fmts["__CARMAR_MIME__"].for_type_by_name(mod, "DataFrame", _frame)
|
|
139
|
+
|
|
140
|
+
# ---- figures -------------------------------------------------------
|
|
141
|
+
# The R worker captures a plot without being asked; Python must too, or
|
|
142
|
+
# `plt.plot(...)` produces nothing and the user is told to type a magic.
|
|
143
|
+
# Both halves are LAZY: neither imports matplotlib, so a session that never
|
|
144
|
+
# plots pays nothing.
|
|
145
|
+
#
|
|
146
|
+
# (1) a PNG formatter for Figure, so an explicitly displayed figure renders
|
|
147
|
+
# (2) a post-cell hook that shows figures the user created but never
|
|
148
|
+
# displayed — what %matplotlib inline does, without the magic
|
|
149
|
+
|
|
150
|
+
def _fig_png(fig):
|
|
151
|
+
import io, base64
|
|
152
|
+
buf = io.BytesIO()
|
|
153
|
+
fig.savefig(buf, format="png", dpi=96, bbox_inches="tight")
|
|
154
|
+
return base64.b64encode(buf.getvalue()).decode()
|
|
155
|
+
|
|
156
|
+
png = fmts.get("image/png")
|
|
157
|
+
if png is not None:
|
|
158
|
+
png.for_type_by_name("matplotlib.figure", "Figure", _fig_png)
|
|
159
|
+
|
|
160
|
+
def _flush_figures(*a, **k):
|
|
161
|
+
import sys
|
|
162
|
+
plt = sys.modules.get("matplotlib.pyplot")
|
|
163
|
+
if plt is None:
|
|
164
|
+
return
|
|
165
|
+
try:
|
|
166
|
+
from IPython.display import display
|
|
167
|
+
for num in plt.get_fignums():
|
|
168
|
+
display(plt.figure(num))
|
|
169
|
+
plt.close("all")
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
ip.events.register("post_run_cell", _flush_figures)
|
|
174
|
+
|
|
175
|
+
# ---- the working directory, reported without being asked --------------
|
|
176
|
+
# Every relative path the host resolves is resolved against the WORKER's
|
|
177
|
+
# cwd, and an `os.chdir()` inside a cell moves it. R gets this free — its
|
|
178
|
+
# worker prints getwd() into the done frame it is already building. Here
|
|
179
|
+
# the frame is built outside the kernel, so the kernel has to volunteer the
|
|
180
|
+
# value, and post_run_cell is the one place it is certainly current: the
|
|
181
|
+
# cell that moved it has finished.
|
|
182
|
+
def _carmapy_cwd(*a, **k):
|
|
183
|
+
import os, json
|
|
184
|
+
from IPython.display import publish_display_data
|
|
185
|
+
publish_display_data({"__CARMAPY_CONTROL__": json.dumps({"cwd": os.getcwd()})})
|
|
186
|
+
|
|
187
|
+
ip.events.register("post_run_cell", _carmapy_cwd)
|
|
188
|
+
|
|
189
|
+
# ---- the data viewer, and the environment listing ----------------------
|
|
190
|
+
# `dataview.py`'s source, exec'd on FIRST USE and never at boot. Lazy so a
|
|
191
|
+
# session that never opens the viewer does not pay pandas' import to start.
|
|
192
|
+
state = {"ns": None}
|
|
193
|
+
|
|
194
|
+
def _dataview():
|
|
195
|
+
if state["ns"] is None:
|
|
196
|
+
import base64
|
|
197
|
+
ns = {"__name__": "carmapy_dataview"}
|
|
198
|
+
exec(compile(base64.b64decode("__CARMAPY_DATAVIEW_B64__").decode("utf-8"),
|
|
199
|
+
"<beatrina dataview>", "exec"), ns)
|
|
200
|
+
state["ns"] = ns
|
|
201
|
+
return state["ns"]
|
|
202
|
+
|
|
203
|
+
def _env_rows(user_ns):
|
|
204
|
+
"""The Environment pane's rows, in `env`'s shape (spike/worker.R).
|
|
205
|
+
|
|
206
|
+
One row per binding the USER made: IPython's own names (`In`, `Out`,
|
|
207
|
+
`_i3`, `get_ipython`), dunders and this module's own helpers are not
|
|
208
|
+
the user's variables and would bury them. `bytes` is sys.getsizeof,
|
|
209
|
+
which is the object's own footprint and not a deep size — R's
|
|
210
|
+
object.size is not a deep size either, and claiming one would be worse
|
|
211
|
+
than reporting the shallow one both languages actually have.
|
|
212
|
+
"""
|
|
213
|
+
import sys, types
|
|
214
|
+
hidden = {"In", "Out", "get_ipython", "exit", "quit", "open", "share", "use"}
|
|
215
|
+
rows = []
|
|
216
|
+
for name, value in list(user_ns.items()):
|
|
217
|
+
if name.startswith("_") or name in hidden or name.startswith("__carmapy"):
|
|
218
|
+
continue
|
|
219
|
+
# An `import pandas as pd` IS a binding the user made, but a pane
|
|
220
|
+
# that lists every module buries the three variables the notebook
|
|
221
|
+
# is about. R's Environment pane has the same rule by accident —
|
|
222
|
+
# a package is attached, not bound — so this makes it explicit.
|
|
223
|
+
if isinstance(value, types.ModuleType):
|
|
224
|
+
continue
|
|
225
|
+
cls = type(value).__name__
|
|
226
|
+
try:
|
|
227
|
+
shape = getattr(value, "shape", None)
|
|
228
|
+
if shape is not None:
|
|
229
|
+
dims = "×".join(str(int(d)) for d in shape)
|
|
230
|
+
elif isinstance(value, (list, tuple, dict, set, str, bytes)):
|
|
231
|
+
dims = str(len(value))
|
|
232
|
+
else:
|
|
233
|
+
dims = ""
|
|
234
|
+
except Exception:
|
|
235
|
+
dims = ""
|
|
236
|
+
try:
|
|
237
|
+
summary = repr(value)
|
|
238
|
+
if len(summary) > 120:
|
|
239
|
+
summary = summary[:120] + "…"
|
|
240
|
+
except Exception:
|
|
241
|
+
summary = "<unrepresentable>"
|
|
242
|
+
kind = "function" if callable(value) else "value"
|
|
243
|
+
try:
|
|
244
|
+
nbytes = int(sys.getsizeof(value))
|
|
245
|
+
except Exception:
|
|
246
|
+
nbytes = 0
|
|
247
|
+
rows.append({"name": name, "class": cls, "dims": dims,
|
|
248
|
+
"bytes": nbytes, "summary": summary, "kind": kind})
|
|
249
|
+
rows.sort(key=lambda r: r["name"])
|
|
250
|
+
return {"objects": rows}
|
|
251
|
+
|
|
252
|
+
def __carmapy_reply(op, spec_json):
|
|
253
|
+
"""Answer one request into a private MIME on iopub.
|
|
254
|
+
|
|
255
|
+
NOT a return value and not stdout. A display_data message carries the
|
|
256
|
+
parent msg_id, so the worker can match the answer to the request it
|
|
257
|
+
sent — and user code printing the same text cannot be mistaken for a
|
|
258
|
+
reply, which is the property R buys with its 24-character sentinel.
|
|
259
|
+
"""
|
|
260
|
+
import json
|
|
261
|
+
from IPython.display import publish_display_data
|
|
262
|
+
try:
|
|
263
|
+
spec = json.loads(spec_json)
|
|
264
|
+
if op == "env":
|
|
265
|
+
payload = _env_rows(get_ipython().user_ns)
|
|
266
|
+
elif op == "rm":
|
|
267
|
+
ns = get_ipython().user_ns
|
|
268
|
+
names = spec.get("names") or []
|
|
269
|
+
if isinstance(names, str):
|
|
270
|
+
names = [names]
|
|
271
|
+
for nm in names:
|
|
272
|
+
ns.pop(nm, None)
|
|
273
|
+
payload = {"removed": True}
|
|
274
|
+
else:
|
|
275
|
+
dv = _dataview()
|
|
276
|
+
fn = dv["view_reply"] if op == "view" else dv["colstats_reply"]
|
|
277
|
+
payload = fn(spec, get_ipython().user_ns)
|
|
278
|
+
except Exception as exc:
|
|
279
|
+
# An error FIELD, never a raised exception: pointing the viewer at
|
|
280
|
+
# a 40-column frame of fitted models is a normal thing to do by
|
|
281
|
+
# accident, and it must cost a message, not the session.
|
|
282
|
+
payload = {"error": "%s: %s" % (type(exc).__name__, exc)}
|
|
283
|
+
publish_display_data({"__CARMAPY_REPLY__": json.dumps(payload, allow_nan=False)})
|
|
284
|
+
|
|
285
|
+
ip.user_ns["__carmapy_reply"] = __carmapy_reply
|
|
286
|
+
|
|
287
|
+
# ---- the debugger (docs/wp/wp8b-python-debugger.md) ------------------
|
|
288
|
+
import base64 as _b64
|
|
289
|
+
dbg = {"__name__": "carmar_debugger"}
|
|
290
|
+
exec(compile(_b64.b64decode("__CARMAR_DEBUGGER_B64__").decode("utf-8"),
|
|
291
|
+
"<beatrina debugger>", "exec"), dbg)
|
|
292
|
+
dbg["install"](ip)
|
|
293
|
+
ip.user_ns["__carmar_debug_prepare"] = dbg["prepare"]
|
|
294
|
+
|
|
295
|
+
# ---- share / use (engines/python/handoff.py) -------------------------
|
|
296
|
+
hand = {"__name__": "beatrix_handoff"}
|
|
297
|
+
exec(compile(_b64.b64decode("__BEATRIX_HANDOFF_B64__").decode("utf-8"),
|
|
298
|
+
"<beatrina handoff>", "exec"), hand)
|
|
299
|
+
ip.user_ns["share"] = hand["share"]
|
|
300
|
+
ip.user_ns["use"] = hand["use"]
|
|
301
|
+
|
|
302
|
+
__carmar_install_formatters()
|
|
303
|
+
del __carmar_install_formatters
|
|
304
|
+
'''.replace("__CARMAR_MIME__", CARMAR_MIME) \
|
|
305
|
+
.replace("__CARMAPY_REPLY__", REPLY_MIME) \
|
|
306
|
+
.replace("__CARMAPY_CONTROL__", CONTROL_MIME) \
|
|
307
|
+
.replace("__CARMAPY_DATAVIEW_B64__", _DATAVIEW_B64) \
|
|
308
|
+
.replace("__CARMAR_DEBUGGER_B64__", _DEBUGGER_B64) \
|
|
309
|
+
.replace("__BEATRIX_HANDOFF_B64__", _HANDOFF_B64)
|