nuspace 0.6.2__tar.gz → 0.6.4__tar.gz
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.
- {nuspace-0.6.2 → nuspace-0.6.4}/CHANGELOG.md +22 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/PKG-INFO +6 -6
- {nuspace-0.6.2 → nuspace-0.6.4}/pyproject.toml +7 -6
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/__init__.py +2 -4
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_telemetry/__init__.py +7 -5
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_telemetry/config.py +18 -4
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_telemetry/send.py +44 -9
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/content/kind.py +2 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/content/search.py +12 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ext/__init__.py +1 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ext/specs.py +31 -12
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/host/presets.py +2 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/host/space.py +5 -4
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/__init__.py +2 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/cell.py +1 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/kernel.py +18 -12
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/read.py +7 -5
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/state.py +2 -1
- nuspace-0.6.4/src/nuspace/ops/stores.py +51 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/tree.py +2 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/utils.py +18 -8
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/backends/base.py +4 -5
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/backends/pool.py +64 -45
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/shell.py +2 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/utils.py +20 -50
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/viewer/feed.py +50 -34
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/viewer/ref.py +26 -12
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/body.py +2 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/envs.py +1 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/kernel.py +6 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/out.py +4 -5
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/reconcile.py +8 -18
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/runs.py +90 -18
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/space.py +22 -14
- nuspace-0.6.4/src/nuspace/system/kernel/utils.py +8 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/bootstrap.py +2 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/init.py +30 -21
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/nav.py +60 -40
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/reactions.py +44 -32
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/reload.py +8 -5
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/supervisor.py +49 -60
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/ui/home.py +22 -18
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/ui/search.py +25 -21
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/ui/seed.py +1 -1
- nuspace-0.6.4/src/nuspace/system/utils.py +67 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ui.py +3 -3
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/kernel.py +2 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/specs.py +1 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/conftest.py +10 -2
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_cli.py +2 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_content.py +1 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_home.py +8 -57
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_host.py +12 -3
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_kernel.py +132 -4
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_ops_state.py +14 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_reactions.py +104 -37
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_search.py +145 -6
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_services.py +98 -7
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_shapes.py +7 -6
- nuspace-0.6.4/tests/nuspace/test_system_utils.py +129 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_telemetry.py +39 -13
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_ui.py +1 -1
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_web.py +59 -9
- nuspace-0.6.2/src/nuspace/system/kernel/utils.py +0 -24
- nuspace-0.6.2/src/nuspace/system/utils.py +0 -106
- {nuspace-0.6.2 → nuspace-0.6.4}/.gitignore +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/.pre-commit-config.yaml +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/LICENSE.md +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/Makefile +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/README.md +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/examples/space.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/pkgs/ts/nuspace-ui/python/nuspace_ui/build +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_branding/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_branding/header.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_branding/logo.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_branding/palette.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/__main__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/_installed.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/_meta.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/commands/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/commands/init.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/commands/run.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/commands/telemetry.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/commands/verse.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/_cli/main.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/content/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ext/registry.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/host/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/extend.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/pin.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/plane.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/ops/usage.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/cell.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/connection.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/contents.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/kernel.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/plane.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/reroot.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/space.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/state.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/shapes/states.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/backends/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/backends/async_.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/backends/mp.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/device.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/env.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/session.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/sidebar/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/sidebar/feed.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/sidebar/interactions.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/sidebar/ref.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/viewer/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/devices/web/viewer/interactions.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/info.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/kernel/dispatch.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/services/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/ui/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/src/nuspace/system/ui/settings.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_ext/nuspace_testing/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_ext/pyproject.toml +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/__init__.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/actions.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/contents.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/kernel_envs.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/made.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/_lib/_support/probe.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_ops_kernel.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_ops_structure.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_packaging.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_session_pipe.py +0 -0
- {nuspace-0.6.2 → nuspace-0.6.4}/tests/nuspace/test_settings.py +0 -0
|
@@ -2,13 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Packages shipped from this repo
|
|
4
4
|
|
|
5
|
-
- **nuspace** (the runtime, the `nuspace` command) — 0.6.
|
|
6
|
-
- **nuspace-ui** (the compiled web bundle) — 0.
|
|
5
|
+
- **nuspace** (the runtime, the `nuspace` command) — 0.6.4
|
|
6
|
+
- **nuspace-ui** (the compiled web bundle) — 0.6.4
|
|
7
7
|
|
|
8
8
|
Below is the changelog for **nuspace** - the full commit stream. Newest first.
|
|
9
9
|
The `nuspace-ui` wheel is the same source tree's vite output and is not tracked
|
|
10
10
|
separately here.
|
|
11
11
|
|
|
12
|
+
## 0.6.4 - 2026-11-07
|
|
13
|
+
|
|
14
|
+
- Release nuspace and nuspace-ui 0.6.4 on nu 0.7.1, virtuals 0.2.1 and ui-kit 0.4.0
|
|
15
|
+
- Telemetry sends only known nuspace events and stays off in every major CI
|
|
16
|
+
- Cmd+Enter at the end of text leaves no empty line behind
|
|
17
|
+
- Writing keeps to prose islands: arrows stop at other cells, input lines join in
|
|
18
|
+
- Plane menus share one layout; sidebar menu gains copy id and close items
|
|
19
|
+
- Text cells read as one document: arrows cross seams, Backspace merges, Cmd+Enter splits
|
|
20
|
+
- Writing in a plane: honest hints, slash in empty text, arrows across text cells
|
|
21
|
+
- Move onto the new reactive flows; failing arms now restart, isolate or fail their run
|
|
22
|
+
- Workers come from the hardened pool: one stop grace, no orphans, clean exit causes
|
|
23
|
+
|
|
24
|
+
## 0.6.3 - 2026-11-05
|
|
25
|
+
|
|
26
|
+
- Release nuspace and nuspace-ui 0.6.3 on nu 0.7.0 and virtuals 0.2.0
|
|
27
|
+
- Reads of containers that may never exist give empty instead of failing
|
|
28
|
+
- A space creates every fixed store container at open, so reads find them
|
|
29
|
+
- Kernel, supervisor and boot list create their containers up front at open
|
|
30
|
+
- Move onto nu's NULL model: absent reads checked by existence, safe defaults
|
|
31
|
+
|
|
12
32
|
## 0.6.2 — 2026-10-30
|
|
13
33
|
|
|
14
34
|
- Release nuspace 0.6.2 on nu 0.6.8 and nuverse 0.3.5
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nuspace
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Computational space for your data and tools.
|
|
5
5
|
Project-URL: Repository, https://github.com/nustackdev/nuspace
|
|
6
6
|
Author-email: Gor Arakelyan <gorarkln@gmail.com>
|
|
@@ -17,16 +17,16 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
18
|
Requires-Dist: certifi>=2023.7.22
|
|
19
19
|
Requires-Dist: click>=8.0
|
|
20
|
-
Requires-Dist: nucore>=0.
|
|
21
|
-
Requires-Dist: nuspace-ui>=0.
|
|
22
|
-
Requires-Dist: nustd[kv,mp-pool,proxy,ui,valkey]>=0.
|
|
20
|
+
Requires-Dist: nucore>=0.7.1
|
|
21
|
+
Requires-Dist: nuspace-ui>=0.6.4
|
|
22
|
+
Requires-Dist: nustd[kv,mp-pool,proxy,ui,valkey]>=0.7.1
|
|
23
23
|
Requires-Dist: nuverse>=0.3.5
|
|
24
24
|
Requires-Dist: rich-click>=1.6
|
|
25
25
|
Requires-Dist: rich>=10.7
|
|
26
26
|
Requires-Dist: typing-extensions>=4.7.0
|
|
27
|
-
Requires-Dist: virtuals-py[redis]>=0.1
|
|
27
|
+
Requires-Dist: virtuals-py[redis]>=0.2.1
|
|
28
28
|
Provides-Extra: all
|
|
29
|
-
Requires-Dist: nustd[all]>=0.
|
|
29
|
+
Requires-Dist: nustd[all]>=0.7.1; extra == 'all'
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
32
|
<div align="center">
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nuspace"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.4"
|
|
8
8
|
description = "Computational space for your data and tools."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-or-later"
|
|
@@ -14,14 +14,14 @@ dependencies = [
|
|
|
14
14
|
# The fabrics a space runs on, nothing more: kv for the store, valkey for
|
|
15
15
|
# its change notifications, ui for the browser, mp_pool for the workers,
|
|
16
16
|
# proxy (invisibles) for the worker's session.
|
|
17
|
-
"nucore>=0.
|
|
18
|
-
"nustd[kv,ui,valkey,mp_pool,proxy]>=0.
|
|
17
|
+
"nucore>=0.7.1",
|
|
18
|
+
"nustd[kv,ui,valkey,mp_pool,proxy]>=0.7.1",
|
|
19
19
|
# The store's change notifications run over virtuals' redis backend.
|
|
20
|
-
"virtuals-py[redis]>=0.1
|
|
20
|
+
"virtuals-py[redis]>=0.2.1",
|
|
21
21
|
# The compiled web bundle. A separate distribution only because PyPI caps
|
|
22
22
|
# project size and a SPA rebuilt on every UI tweak would eat the quota --
|
|
23
23
|
# not optional. `pip install nuspace` gives you the browser.
|
|
24
|
-
"nuspace-ui>=0.
|
|
24
|
+
"nuspace-ui>=0.6.4",
|
|
25
25
|
# The everyday Planes and Cells. A plugin like any other, found through
|
|
26
26
|
# its entry point, but every space ships with it.
|
|
27
27
|
"nuverse>=0.3.5",
|
|
@@ -55,7 +55,7 @@ classifiers = [
|
|
|
55
55
|
[project.optional-dependencies]
|
|
56
56
|
# Every nustd fabric and kv backend, for cells that reach past the defaults.
|
|
57
57
|
all = [
|
|
58
|
-
"nustd[all]>=0.
|
|
58
|
+
"nustd[all]>=0.7.1",
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
[project.urls]
|
|
@@ -76,6 +76,7 @@ exclude = [
|
|
|
76
76
|
]
|
|
77
77
|
|
|
78
78
|
[tool.uv.sources]
|
|
79
|
+
virtuals-py = { path = "../virtuals", editable = true }
|
|
79
80
|
nuspace-ui = { path = "./pkgs/ts/nuspace-ui", editable = true }
|
|
80
81
|
nucore = { path = "../nu", editable = true }
|
|
81
82
|
nustd = { path = "../nu/pkgs/nustd", editable = true }
|
|
@@ -5,8 +5,7 @@ What a host and a program need, without knowing the layers:
|
|
|
5
5
|
- :func:`open_space`, and what an installed package registers with: an
|
|
6
6
|
:class:`Extension` of :class:`PlaneSpec`, :class:`CellSpec` and
|
|
7
7
|
:class:`ContentSpec` (the store's own shapes are ``shapes.Plane`` and
|
|
8
|
-
``shapes.Cell``)
|
|
9
|
-
document editing works on;
|
|
8
|
+
``shapes.Cell``);
|
|
10
9
|
- :class:`Content`, a kind of findable item, ``class
|
|
11
10
|
TextContent(nuspace.Content[Text], name="Text")``, registered through a
|
|
12
11
|
:class:`ContentSpec` (:mod:`nuspace.content`);
|
|
@@ -23,7 +22,7 @@ Workers import this package: nothing here may pull in a server.
|
|
|
23
22
|
|
|
24
23
|
from nuspace import content, ops, shapes, ui
|
|
25
24
|
from nuspace.content import Content
|
|
26
|
-
from nuspace.ext import
|
|
25
|
+
from nuspace.ext import CellSpec, ContentSpec, Extension, PlaneSpec
|
|
27
26
|
from nuspace.host import open_space
|
|
28
27
|
from nuspace.ops import env
|
|
29
28
|
from nuspace.shapes import CellState, PlaneState, Space, States
|
|
@@ -38,7 +37,6 @@ from nuspace.system.services import (
|
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
__all__ = [
|
|
41
|
-
"TEXT_CELL",
|
|
42
40
|
"CellSpec",
|
|
43
41
|
"CellState",
|
|
44
42
|
"Content",
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
What is sent: ``first_run`` once per user and ``space_opened`` per open, by
|
|
9
9
|
the host however the space is opened (:func:`.opened`), ``space_loaded`` per
|
|
10
10
|
page load in the browser (through the web device, never from the browser
|
|
11
|
-
itself),
|
|
11
|
+
itself), ``plane_created``, ``plane_removed``, ``cell_created``,
|
|
12
12
|
``cell_removed`` by the ops that do it, in whatever process runs them
|
|
13
|
-
(:mod:`nuspace.ops.usage`)
|
|
14
|
-
|
|
13
|
+
(:mod:`nuspace.ops.usage`), and the ``quickstart_*`` steps nuverse tracks
|
|
14
|
+
through those ops. Each carries the versions and the machine's kind,
|
|
15
|
+
nothing about the space. Those names, :data:`.send.EVENTS`, are all that is
|
|
16
|
+
ever sent: any other is dropped before it is queued.
|
|
15
17
|
|
|
16
18
|
Stdlib only and silent on every failure. Queuing is free and never sends:
|
|
17
19
|
the sender drops everything when telemetry is off, on a dev install, under
|
|
@@ -28,14 +30,14 @@ from .config import (
|
|
|
28
30
|
set_telemetry,
|
|
29
31
|
telemetry_enabled,
|
|
30
32
|
)
|
|
31
|
-
from .send import PREFIX, base_properties,
|
|
33
|
+
from .send import EVENTS, PREFIX, base_properties, first_run, flush, opened, track
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
__all__ = [
|
|
35
37
|
"ENV",
|
|
38
|
+
"EVENTS",
|
|
36
39
|
"PREFIX",
|
|
37
40
|
"base_properties",
|
|
38
|
-
"capture",
|
|
39
41
|
"config_path",
|
|
40
42
|
"distinct_id",
|
|
41
43
|
"env_override",
|
|
@@ -7,8 +7,9 @@ test or a sandbox pointing ``HOME`` elsewhere gets its own.
|
|
|
7
7
|
|
|
8
8
|
The environment only ever turns telemetry off, whatever the file says:
|
|
9
9
|
``NUSPACE_TELEMETRY`` set to ``0``, ``false`` or ``off``, ``DO_NOT_TRACK``
|
|
10
|
-
set to anything but that (the cross tool convention), or ``CI``
|
|
11
|
-
|
|
10
|
+
set to anything but that (the cross tool convention), or ``CI`` or a major
|
|
11
|
+
CI's own variable (``GITHUB_ACTIONS``, ``JENKINS_URL``, ...) set the same
|
|
12
|
+
way, so nobody's pipeline is counted as use.
|
|
12
13
|
"""
|
|
13
14
|
|
|
14
15
|
from __future__ import annotations
|
|
@@ -42,8 +43,21 @@ ENV = "NUSPACE_TELEMETRY"
|
|
|
42
43
|
#: What ``NUSPACE_TELEMETRY`` reads as off, case ignored.
|
|
43
44
|
_OFF = frozenset({"0", "false", "off", "no"})
|
|
44
45
|
|
|
45
|
-
#: Conventions that turn telemetry off when set to anything but empty or off
|
|
46
|
-
|
|
46
|
+
#: Conventions that turn telemetry off when set to anything but empty or off:
|
|
47
|
+
#: the cross tool one, then ``CI`` and the variables CIs set of their own.
|
|
48
|
+
_OFF_WHEN_SET = (
|
|
49
|
+
"DO_NOT_TRACK",
|
|
50
|
+
"CI",
|
|
51
|
+
"GITHUB_ACTIONS",
|
|
52
|
+
"GITLAB_CI",
|
|
53
|
+
"BUILDKITE",
|
|
54
|
+
"CIRCLECI",
|
|
55
|
+
"TF_BUILD",
|
|
56
|
+
"JENKINS_URL",
|
|
57
|
+
"TEAMCITY_VERSION",
|
|
58
|
+
"CODEBUILD_BUILD_ID",
|
|
59
|
+
"BITBUCKET_BUILD_NUMBER",
|
|
60
|
+
)
|
|
47
61
|
|
|
48
62
|
|
|
49
63
|
def home() -> Path:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Fire and forget PostHog capture, no dependency beyond the stdlib.
|
|
2
2
|
|
|
3
|
-
One sender thread per process, fed by a bounded queue. :func:`
|
|
3
|
+
One sender thread per process, fed by a bounded queue. :func:`track`
|
|
4
4
|
puts the event's name and time on the queue and returns, O(1): it never
|
|
5
5
|
waits, never touches the network or the disk, and drops the event when the
|
|
6
6
|
queue is full. The thread does the rest, the properties, the id, the opt out
|
|
@@ -9,9 +9,14 @@ and the JSON, and drains what is queued into one ``/batch/`` POST at a time. Eve
|
|
|
9
9
|
breaks because of telemetry. At exit the thread gets a short grace to send
|
|
10
10
|
what is left.
|
|
11
11
|
|
|
12
|
+
Only nuspace's own events are ever queued: a name not in :data:`EVENTS`,
|
|
13
|
+
or anything but a str, is dropped before it reaches the queue, so no caller
|
|
14
|
+
can send a name of its own.
|
|
15
|
+
|
|
12
16
|
Nothing is sent when telemetry is off (see :mod:`.config`: the flag, or
|
|
13
|
-
``NUSPACE_TELEMETRY``, ``DO_NOT_TRACK
|
|
14
|
-
|
|
17
|
+
``NUSPACE_TELEMETRY``, ``DO_NOT_TRACK``, ``CI`` or a CI's own variable), on
|
|
18
|
+
a dev install, or with no token: the thread checks before each POST and
|
|
19
|
+
drops the batch. ``NUSPACE_POSTHOG_TOKEN`` and ``NUSPACE_POSTHOG_HOST`` point it at
|
|
15
20
|
another PostHog project, eg to test.
|
|
16
21
|
"""
|
|
17
22
|
|
|
@@ -40,10 +45,10 @@ if TYPE_CHECKING:
|
|
|
40
45
|
|
|
41
46
|
|
|
42
47
|
__all__ = [
|
|
48
|
+
"EVENTS",
|
|
43
49
|
"FIRST_RUN_EVENT",
|
|
44
50
|
"PREFIX",
|
|
45
51
|
"base_properties",
|
|
46
|
-
"capture",
|
|
47
52
|
"first_run",
|
|
48
53
|
"flush",
|
|
49
54
|
"opened",
|
|
@@ -65,6 +70,28 @@ _BATCH_PATH = "/batch/"
|
|
|
65
70
|
#: What every event is sent as: the product, then what happened.
|
|
66
71
|
PREFIX = "nuspace_"
|
|
67
72
|
|
|
73
|
+
#: Every event that may be sent, by its name before :data:`PREFIX`. Nothing else ever is.
|
|
74
|
+
EVENTS = frozenset(
|
|
75
|
+
{
|
|
76
|
+
"first_run",
|
|
77
|
+
"space_opened",
|
|
78
|
+
"space_loaded",
|
|
79
|
+
"plane_created",
|
|
80
|
+
"plane_removed",
|
|
81
|
+
"cell_created",
|
|
82
|
+
"cell_removed",
|
|
83
|
+
# nuverse's quickstart, through ``nuspace.ops.track``.
|
|
84
|
+
"quickstart_demo_opened",
|
|
85
|
+
"quickstart_page_made",
|
|
86
|
+
"quickstart_edited",
|
|
87
|
+
"quickstart_put_back",
|
|
88
|
+
"quickstart_moved",
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
#: :data:`EVENTS` as sent, prefixed.
|
|
93
|
+
_SENT = frozenset(f"{PREFIX}{event}" for event in EVENTS)
|
|
94
|
+
|
|
68
95
|
#: Sent once per user, retried on every open until PostHog acks it.
|
|
69
96
|
FIRST_RUN_EVENT = f"{PREFIX}first_run"
|
|
70
97
|
|
|
@@ -253,11 +280,16 @@ def _the_sender() -> _Sender:
|
|
|
253
280
|
|
|
254
281
|
|
|
255
282
|
def _queue(event: str, on_success: Callable[[], None] | None) -> None:
|
|
256
|
-
|
|
283
|
+
"""Put ``event``, a prefixed name, on the queue. Every send goes through here.
|
|
284
|
+
|
|
285
|
+
A name not in :data:`EVENTS`, or anything but a str, is dropped.
|
|
286
|
+
"""
|
|
287
|
+
if isinstance(event, str) and event in _SENT:
|
|
288
|
+
_the_sender().put((event, time.time(), on_success))
|
|
257
289
|
|
|
258
290
|
|
|
259
|
-
def
|
|
260
|
-
"""Queue ``event
|
|
291
|
+
def _capture(event: str) -> None:
|
|
292
|
+
"""Queue ``event``, a prefixed name, for the sender thread. O(1), never waits, never raises."""
|
|
261
293
|
try:
|
|
262
294
|
_queue(event, None)
|
|
263
295
|
except Exception: # noqa: S110
|
|
@@ -265,9 +297,12 @@ def capture(event: str) -> None:
|
|
|
265
297
|
|
|
266
298
|
|
|
267
299
|
def track(event: str) -> None:
|
|
268
|
-
"""
|
|
300
|
+
"""Queue ``event`` as nuspace's own, eg ``plane_created``. Never raises.
|
|
301
|
+
|
|
302
|
+
Anything not in :data:`EVENTS`, or not a str, is dropped.
|
|
303
|
+
"""
|
|
269
304
|
if isinstance(event, str):
|
|
270
|
-
|
|
305
|
+
_capture(f"{PREFIX}{event}")
|
|
271
306
|
|
|
272
307
|
|
|
273
308
|
def flush(timeout: float = _EXIT_GRACE_SECONDS) -> bool:
|
|
@@ -304,7 +304,8 @@ class Content(Generic[S]):
|
|
|
304
304
|
``items(scope)[id]`` is :meth:`at`. The mapping is the store's own, so
|
|
305
305
|
``keys``, ``len``, ``contains`` and ``on_children_change`` read and
|
|
306
306
|
watch storage, a write lands an item and ``items(scope)[id].erase()``
|
|
307
|
-
removes one.
|
|
307
|
+
removes one. A scope no item ever landed in has no mapping yet:
|
|
308
|
+
``exists()`` is False and ``len`` or ``keys`` raise. Not relative: name the running cell as
|
|
308
309
|
``(CELL, ops.Here.cell)``.
|
|
309
310
|
|
|
310
311
|
Args:
|
|
@@ -31,6 +31,15 @@ if TYPE_CHECKING:
|
|
|
31
31
|
__all__ = ["asked", "matched", "scopes", "walk"]
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def _ids(held: nu.Nu) -> nu.List:
|
|
35
|
+
"""A mapping's keys, ``[]`` when it was never written.
|
|
36
|
+
|
|
37
|
+
A kind's level, or one scope of it, exists only once an item landed
|
|
38
|
+
there: a kind never written in a plane has no ``planes`` to list.
|
|
39
|
+
"""
|
|
40
|
+
return nu.list(nu.If(held.exists(), nu.list(held.keys()), []))
|
|
41
|
+
|
|
42
|
+
|
|
34
43
|
def scopes(kind: type[Content[Any]], level: str) -> nu.List:
|
|
35
44
|
"""The ids of the scopes at ``level`` holding items of ``kind``: cells or planes. A bare read.
|
|
36
45
|
|
|
@@ -41,7 +50,8 @@ def scopes(kind: type[Content[Any]], level: str) -> nu.List:
|
|
|
41
50
|
if level not in SLOTS:
|
|
42
51
|
msg = f"scope level {level!r} is none of {tuple(SLOTS)}"
|
|
43
52
|
raise ValueError(msg)
|
|
44
|
-
|
|
53
|
+
held = getattr(States.contents[kind_name(kind)], SLOTS[level])
|
|
54
|
+
return _ids(held)
|
|
45
55
|
|
|
46
56
|
|
|
47
57
|
def matched(
|
|
@@ -59,7 +69,7 @@ def matched(
|
|
|
59
69
|
"""
|
|
60
70
|
held = kind.items(scope)
|
|
61
71
|
return (
|
|
62
|
-
|
|
72
|
+
_ids(held)
|
|
63
73
|
.iter()
|
|
64
74
|
.filter(lambda found: kind.match(kind.at(scope, nu.Str(found)), **question))
|
|
65
75
|
)
|
|
@@ -27,13 +27,12 @@ from .registry import (
|
|
|
27
27
|
short_name,
|
|
28
28
|
space_registry,
|
|
29
29
|
)
|
|
30
|
-
from .specs import EMPTY_PLANE,
|
|
30
|
+
from .specs import EMPTY_PLANE, CellSpec, ContentSpec, PlaneSpec
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
__all__ = [
|
|
34
34
|
"EMPTY_PLANE",
|
|
35
35
|
"GROUP",
|
|
36
|
-
"TEXT_CELL",
|
|
37
36
|
"CellSpec",
|
|
38
37
|
"ContentSpec",
|
|
39
38
|
"Extension",
|
|
@@ -20,12 +20,7 @@ if TYPE_CHECKING:
|
|
|
20
20
|
from nuspace.content import Content
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
__all__ = ["EMPTY_PLANE", "
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#: The ``nuverse.text`` Cell: nuspace's document editing works on its cells.
|
|
27
|
-
#: See :class:`CellSpec`.
|
|
28
|
-
TEXT_CELL = "nuverse.text"
|
|
23
|
+
__all__ = ["EMPTY_PLANE", "CellSpec", "ContentSpec", "PlaneSpec"]
|
|
29
24
|
|
|
30
25
|
|
|
31
26
|
@dataclass(frozen=True)
|
|
@@ -91,12 +86,18 @@ EMPTY_PLANE = PlaneSpec(
|
|
|
91
86
|
class CellSpec:
|
|
92
87
|
"""Source for one cell, offered from the ``/`` menu.
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
one
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
A cell made from it keeps the spec's classes: the protocols it promises,
|
|
90
|
+
which the browser's sugar binds to, never to which spec made a cell. A
|
|
91
|
+
class is a name, its promise written here. ``prose``: the cell draws
|
|
92
|
+
exactly one document that registers a kit document handle (a kit
|
|
93
|
+
``MarkdownRef`` does on its own), and its value is markdown. Any spec
|
|
94
|
+
whose cell keeps that promise declares it, whoever wrote it. A cell made
|
|
95
|
+
from no spec, or from one no longer installed, has no classes and gets
|
|
96
|
+
the plane's core behaviour only.
|
|
97
|
+
|
|
98
|
+
One spec per class is its default, what typing on the input line
|
|
99
|
+
creates: a spec claims it with ``default_for``. When two installed specs
|
|
100
|
+
claim the same class, the first in registration order wins, no error.
|
|
100
101
|
|
|
101
102
|
Args:
|
|
102
103
|
name: The new cell's name. Installed by a package, it registers
|
|
@@ -111,6 +112,13 @@ class CellSpec:
|
|
|
111
112
|
come last, under no heading.
|
|
112
113
|
icon: What the menu draws beside it, spelled as :class:`PlaneSpec`'s
|
|
113
114
|
``icon``. ``""`` for the default.
|
|
115
|
+
classes: The protocols its cells keep, ``("prose",)``.
|
|
116
|
+
default_for: The classes it is the default of, each one of
|
|
117
|
+
``classes``.
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
TypeError: ``classes`` or ``default_for`` is a bare string.
|
|
121
|
+
ValueError: ``default_for`` names a class not in ``classes``.
|
|
114
122
|
"""
|
|
115
123
|
|
|
116
124
|
name: str
|
|
@@ -119,6 +127,17 @@ class CellSpec:
|
|
|
119
127
|
description: str = ""
|
|
120
128
|
group: str = ""
|
|
121
129
|
icon: str = ""
|
|
130
|
+
classes: tuple[str, ...] = ()
|
|
131
|
+
default_for: tuple[str, ...] = ()
|
|
132
|
+
|
|
133
|
+
def __post_init__(self) -> None:
|
|
134
|
+
if isinstance(self.classes, str) or isinstance(self.default_for, str):
|
|
135
|
+
msg = f"CellSpec {self.name!r}: classes and default_for are tuples, eg ('prose',)"
|
|
136
|
+
raise TypeError(msg)
|
|
137
|
+
stray = [c for c in self.default_for if c not in self.classes]
|
|
138
|
+
if stray:
|
|
139
|
+
msg = f"CellSpec {self.name!r}: default_for {stray} not among classes {self.classes}"
|
|
140
|
+
raise ValueError(msg)
|
|
122
141
|
|
|
123
142
|
|
|
124
143
|
@dataclass(frozen=True)
|
|
@@ -23,7 +23,7 @@ from nuspace import ops
|
|
|
23
23
|
from nuspace.ext.registry import registered_name
|
|
24
24
|
from nuspace.ops.utils import atomic
|
|
25
25
|
from nuspace.shapes import Space
|
|
26
|
-
from nuspace.system.utils import
|
|
26
|
+
from nuspace.system.utils import snap
|
|
27
27
|
|
|
28
28
|
from .space import open_space
|
|
29
29
|
|
|
@@ -165,7 +165,7 @@ def with_plane(
|
|
|
165
165
|
]
|
|
166
166
|
if web and open_browser:
|
|
167
167
|
steps.append(Browse(nu.Str(f"http://{host}:{port}/") + pid, host, port))
|
|
168
|
-
return nu.Sequential(*steps,
|
|
168
|
+
return nu.Sequential(*steps, nu.Park())
|
|
169
169
|
|
|
170
170
|
body = nu.let("", opened)
|
|
171
171
|
if not web:
|
|
@@ -26,7 +26,8 @@ planes are sure to exist on a first open, so the start is sequenced here
|
|
|
26
26
|
instead (the same term, same ``by``).
|
|
27
27
|
|
|
28
28
|
:func:`init_space` is steps 1 and 2 alone, in the store's brackets only: the
|
|
29
|
-
space directory made
|
|
29
|
+
space directory made, its containers created (as reconcile does at open)
|
|
30
|
+
and seeded, and closed again, nothing run.
|
|
30
31
|
|
|
31
32
|
Workers import :mod:`nuspace`, and so this module: the web device, which
|
|
32
33
|
pulls in the web server, is imported only when a space serves.
|
|
@@ -39,6 +40,7 @@ from typing import TYPE_CHECKING
|
|
|
39
40
|
|
|
40
41
|
import nu
|
|
41
42
|
from nuspace.ext import RegistryWarning, space_registry
|
|
43
|
+
from nuspace.ops import init_stores
|
|
42
44
|
from nuspace.ops.usage import opened
|
|
43
45
|
from nuspace.system.info import write_info
|
|
44
46
|
from nuspace.system.kernel import DEFAULT_SPARES, init_start, open_kernel, store
|
|
@@ -48,7 +50,6 @@ from nuspace.system.services.nav import clear_connections
|
|
|
48
50
|
from nuspace.system.ui.home import ensure_home
|
|
49
51
|
from nuspace.system.ui.search import ensure_search
|
|
50
52
|
from nuspace.system.ui.settings import ensure_settings
|
|
51
|
-
from nuspace.system.utils import park
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
if TYPE_CHECKING:
|
|
@@ -92,7 +93,7 @@ def init_space(path: str) -> nu.With:
|
|
|
92
93
|
Raises:
|
|
93
94
|
NotASpace: ``path`` holds something other than a space.
|
|
94
95
|
"""
|
|
95
|
-
return nu.With(store(path), body=seed_space())
|
|
96
|
+
return nu.With(store(path), body=init_stores() >> seed_space())
|
|
96
97
|
|
|
97
98
|
|
|
98
99
|
def open_space(
|
|
@@ -155,7 +156,7 @@ def open_space(
|
|
|
155
156
|
)
|
|
156
157
|
factories = _with_session(factories, web_envs)
|
|
157
158
|
arms.append(served)
|
|
158
|
-
arms.append(
|
|
159
|
+
arms.append(nu.Park() if body is None else body)
|
|
159
160
|
beside = arms[0] if len(arms) == 1 else nu.Race(*arms)
|
|
160
161
|
kwargs = {} if name is None else {"name": name}
|
|
161
162
|
return open_kernel(
|
|
@@ -84,6 +84,7 @@ from .state import (
|
|
|
84
84
|
plane_state,
|
|
85
85
|
sibling,
|
|
86
86
|
)
|
|
87
|
+
from .stores import init_stores
|
|
87
88
|
from .tree import move_plane
|
|
88
89
|
from .usage import track
|
|
89
90
|
from .utils import atomic, atomic_state, mint_ordered_id, snapshot
|
|
@@ -114,6 +115,7 @@ __all__ = [
|
|
|
114
115
|
"create_cell",
|
|
115
116
|
"create_plane",
|
|
116
117
|
"env",
|
|
118
|
+
"init_stores",
|
|
117
119
|
"latest",
|
|
118
120
|
"mint_ordered_id",
|
|
119
121
|
"move_pin",
|
|
@@ -20,11 +20,10 @@ from __future__ import annotations
|
|
|
20
20
|
from typing import TYPE_CHECKING
|
|
21
21
|
|
|
22
22
|
import nu
|
|
23
|
-
import nustd.kv
|
|
24
23
|
from nuspace.shapes import Space
|
|
25
24
|
|
|
26
25
|
from .read import cell_plane, cells, plane_exists
|
|
27
|
-
from .utils import MintId, atomic
|
|
26
|
+
from .utils import MintId, atomic, listed, snapshot
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
if TYPE_CHECKING:
|
|
@@ -48,6 +47,7 @@ __all__ = [
|
|
|
48
47
|
"off_running",
|
|
49
48
|
"plane_interrupt",
|
|
50
49
|
"plane_kill",
|
|
50
|
+
"plane_live",
|
|
51
51
|
"plane_run",
|
|
52
52
|
"plane_stop",
|
|
53
53
|
"run",
|
|
@@ -76,8 +76,13 @@ STOP_GRACE = 10.0
|
|
|
76
76
|
_kernel = Space.kernel
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def
|
|
80
|
-
|
|
79
|
+
def plane_live(plane_id: nu.StrArg) -> nu.List:
|
|
80
|
+
"""A plane's live plane run ids, ``[]`` when it has none. Bare read.
|
|
81
|
+
|
|
82
|
+
Its ``planes_running`` entry is there only while it has live runs (the
|
|
83
|
+
last run out takes it away), so a plane with none has no set to read.
|
|
84
|
+
"""
|
|
85
|
+
return listed(_kernel.planes_running[plane_id].runs)
|
|
81
86
|
|
|
82
87
|
|
|
83
88
|
# --- Running -------------------------------------------------------------------
|
|
@@ -134,10 +139,11 @@ def add_plane_run(
|
|
|
134
139
|
>> row.by.set(by)
|
|
135
140
|
>> row.envs.set(_envs(envs))
|
|
136
141
|
>> row.termination_requested.set(False)
|
|
137
|
-
>> row.cells.init(
|
|
138
|
-
>> row.cells_running.init(
|
|
139
|
-
>> row.latest.init(
|
|
140
|
-
>> row.workers.init(
|
|
142
|
+
>> row.cells.init(nu.Dict.create())
|
|
143
|
+
>> row.cells_running.init(nu.Set.create())
|
|
144
|
+
>> row.latest.init(nu.Dict.create())
|
|
145
|
+
>> row.workers.init(nu.Set.create())
|
|
146
|
+
>> row.workers_running.init(nu.Set.create())
|
|
141
147
|
>> nu.ForEachDo(
|
|
142
148
|
cells(plane_id),
|
|
143
149
|
lambda cell: nu.let(
|
|
@@ -255,8 +261,8 @@ def _until_ended(run_id: nu.StrArg) -> nu.Nu:
|
|
|
255
261
|
Woken by its plane's live runs, which it leaves in the same commit.
|
|
256
262
|
"""
|
|
257
263
|
live = _kernel.planes_running[_kernel.runs[run_id].plane.fallback("")].runs
|
|
258
|
-
return nu.
|
|
259
|
-
|
|
264
|
+
return nu.WaitUntil(
|
|
265
|
+
snapshot(live.on_children_change()), snapshot(_kernel.running.contains(run_id).not_())
|
|
260
266
|
)
|
|
261
267
|
|
|
262
268
|
|
|
@@ -291,7 +297,7 @@ def off_running(run_id: nu.StrArg) -> nu.Nu:
|
|
|
291
297
|
|
|
292
298
|
def live_runs_of(plane_id: nu.StrArg, body: Callable[[nu.Str], nu.Nu]) -> nu.Nu:
|
|
293
299
|
"""``body(run_id)`` for every live plane run of the plane. No bracket."""
|
|
294
|
-
live =
|
|
300
|
+
live = plane_live(plane_id)
|
|
295
301
|
return nu.ForEachDo(live, lambda rid: body(nu.Str(rid)))
|
|
296
302
|
|
|
297
303
|
|
|
@@ -378,7 +384,7 @@ def runs(plane: nu.StrArg | None = None) -> nu.List:
|
|
|
378
384
|
A run's cell runs are :func:`cell_runs`; an ended run is read by id with
|
|
379
385
|
:func:`run`.
|
|
380
386
|
"""
|
|
381
|
-
live = _kernel.running if plane is None else
|
|
387
|
+
live = _kernel.running if plane is None else plane_live(plane)
|
|
382
388
|
return nu.sorted(live).iter().map(lambda rid: _run_row(nu.Str(rid))).to_list()
|
|
383
389
|
|
|
384
390
|
|
|
@@ -14,6 +14,8 @@ from __future__ import annotations
|
|
|
14
14
|
import nu
|
|
15
15
|
from nuspace.shapes import Space
|
|
16
16
|
|
|
17
|
+
from .utils import listed
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
__all__ = [
|
|
19
21
|
"cell_exists",
|
|
@@ -53,7 +55,7 @@ def plane_title(plane_id: nu.StrArg) -> nu.Str:
|
|
|
53
55
|
|
|
54
56
|
def cells(plane_id: nu.StrArg) -> nu.List:
|
|
55
57
|
"""A plane's cell ids, in order. ``[]`` for a plane never written."""
|
|
56
|
-
return
|
|
58
|
+
return listed(Space.planes[plane_id].cells)
|
|
57
59
|
|
|
58
60
|
|
|
59
61
|
def cell_exists(cell_id: nu.StrArg) -> nu.Bool:
|
|
@@ -66,7 +68,7 @@ def cell_named(plane_id: nu.StrArg, name: nu.StrArg) -> nu.Str:
|
|
|
66
68
|
named = (
|
|
67
69
|
cells(plane_id).iter().filter(lambda c: Space.cells[nu.Str(c)].name.fallback("") == name)
|
|
68
70
|
)
|
|
69
|
-
return nu.str(nu.First(named
|
|
71
|
+
return nu.str(nu.First(named, ""))
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def cell_plane(cell_id: nu.StrArg) -> nu.Str:
|
|
@@ -79,7 +81,7 @@ def children(plane_id: nu.StrArg = "") -> nu.List:
|
|
|
79
81
|
|
|
80
82
|
``[]`` for a plane never written.
|
|
81
83
|
"""
|
|
82
|
-
under =
|
|
84
|
+
under = listed(Space.planes[plane_id].children)
|
|
83
85
|
return nu.List(nu.If(nu.Str(plane_id) == "", nu.list(Space.top), under))
|
|
84
86
|
|
|
85
87
|
|
|
@@ -90,7 +92,7 @@ def parent(plane_id: nu.StrArg) -> nu.Str:
|
|
|
90
92
|
|
|
91
93
|
def prog(cell_id: nu.StrArg) -> nu.Str:
|
|
92
94
|
"""A cell's source, ``""`` where there is none."""
|
|
93
|
-
return nu.str(Space.cells[cell_id].prog
|
|
95
|
+
return nu.str(Space.cells[cell_id].prog.fallback(""))
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def plane_rows() -> nu.List:
|
|
@@ -132,7 +134,7 @@ def cell_rows(plane_id: nu.StrArg) -> nu.List:
|
|
|
132
134
|
return nu.Dict.of(
|
|
133
135
|
id=at,
|
|
134
136
|
name=cell.name.fallback(""),
|
|
135
|
-
prog=nu.str(cell.prog
|
|
137
|
+
prog=nu.str(cell.prog.fallback("")),
|
|
136
138
|
props=nu.Dict.of(
|
|
137
139
|
made_by=cell.props.made_by.fallback(""), has_ui=cell.props.has_ui.fallback(True)
|
|
138
140
|
),
|