Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ param.edit_async(...)` for parameter dialogs.

- **`dlw_applications.py`** — generic application host. It prepares typed
recipe slots, assesses recipe readiness on candidate objects and edited
bindings (through the shim's `datalab.recipe_binding`, shared with Desktop),
bindings (through the shim's `datalab.plugins.recipe_binding`, shared with Desktop),
bridges guidata parameter schemas, opens packaged/generated
examples, and commits recipe objects, results, diagnostics, and provenance
transactionally. It contains no Camera- or Pulse-specific paths.
Expand Down
8 changes: 4 additions & 4 deletions doc/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,18 @@ and may implement `materialize_example()`. Recipe slots carry signal/image type,
`RecipeOutcome`; DataLab-Web commits objects, anchored scalar results,
diagnostics, and provenance transactionally.

The Applications dialog presents each recipe as a method card: expected inputs, a readiness status re-assessed when the selection changes, a _Run on selection…_ button, and _Try with this example_ for each example designed for it. Examples without a recipe are listed as datasets. Readiness and binding checks use the same `datalab.recipe_binding` module as DataLab Desktop, copied into the host shim, so both hosts accept the same selections.
The Applications dialog presents each recipe as a method card: expected inputs, a readiness status re-assessed when the selection changes, a _Run on selection…_ button, and _Try with this example_ for each example designed for it. Examples without a recipe are listed as datasets. Readiness and binding checks use the same `datalab.plugins.recipe_binding` module as DataLab Desktop, copied into the host shim, so both hosts accept the same selections.

### Tools and instruments

Application plugins may also declare `TOOLS`, a tuple of `datalab.plugin_tools.PluginTool` values, as on DataLab Desktop. The Applications dialog lists them in a **Tools** section, and the _Plugins_ menu lists them in the plugin's submenu, after its methods and examples. `object_type` places a tool in the signal or image menu (both when `None`), and `selection` (`none`, `exactly_one`, `at_least_one`, `at_least_two`) enables it only when enough objects of that type are selected; otherwise the dialog tells which objects to select.
Application plugins may also declare `TOOLS`, a tuple of `datalab.plugins.tools.PluginTool` values, as on DataLab Desktop. The Applications dialog lists them in a **Tools** section, and the _Plugins_ menu lists them in the plugin's submenu, after its methods and examples. `object_type` places a tool in the signal or image menu (both when `None`), and `selection` (`none`, `exactly_one`, `at_least_one`, `at_least_two`) enables it only when enough objects of that type are selected; otherwise the dialog tells which objects to select.

A tool is opened in one of two ways:

- **Launcher** (`launcher="method_name"`): DataLab calls the plugin method, and awaits it when it is a coroutine, so it can open dialogs with `edit_async()`. `self.get_selected_objects()` returns the objects selected when the tool was launched; objects the tool adds are refreshed and selected afterwards.
- **Instrument** (`instrument="method_name"`): the method returns a `datalab.plugin_instruments.PluginInstrument`, which holds its settings in a guidata `DataSet` and implements `preview()` (an `InstrumentFrame`: signals drawn together, or one image, with a summary and an optional fixed range) and `acquire()` (an `InstrumentAcquisition`: a group title and the acquired objects). DataLab draws the window itself: a live view on the left, the settings form on the right, a _Live_ toggle refreshing the view every `live_interval_ms`, and an _Acquire_ button adding each acquisition to a new group. The instrument instance lives as long as the plugin is registered, so settings are kept between openings. The plugin writes no user interface code, so the same instrument runs on DataLab Desktop and DataLab-Web.
- **Instrument** (`instrument="method_name"`): the method returns a `datalab.plugins.instruments.PluginInstrument`, which holds its settings in a guidata `DataSet` and implements `preview()` (an `InstrumentFrame`: signals drawn together, or one image, with a summary and an optional fixed range) and `acquire()` (an `InstrumentAcquisition`: a group title and the acquired objects). DataLab draws the window itself: a live view on the left, the settings form on the right, a _Live_ toggle refreshing the view every `live_interval_ms`, and an _Acquire_ button adding each acquisition to a new group. The instrument instance lives as long as the plugin is registered, so settings are kept between openings. The plugin writes no user interface code, so the same instrument runs on DataLab Desktop and DataLab-Web.

`datalab.plugin_tools`, `datalab.plugin_instruments` and `datalab.plugin_resources` are copied from DataLab Desktop into the host shim, like `datalab.recipe_binding`.
`datalab.plugins.tools`, `datalab.plugins.instruments` and `datalab.plugins.resources` are copied from DataLab Desktop into the host shim, like `datalab.plugins.recipe_binding`.

Version 1 of the installer deliberately accepts only local `*-none-any`
pure-Python wheels compatible with Pyodide's Python version. Native payloads,
Expand Down
Binary file not shown.
Binary file not shown.
8 changes: 4 additions & 4 deletions src/runtime/bundledPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ export const BUNDLED_PLUGIN_WHEELS: readonly BundledPluginWheel[] =
version: "0.2.0",
filename: "datalab_camera_characterization-0.2.0-py3-none-any.whl",
sha256:
"e89635561bd7b8eb10dd54f4f21ad4358d0b3562c7d016b8066ef1239b56a5c3",
sizeBytes: 332_561,
"d5e483caf43418c5182ac717ce724ba95b973e4aea835be6cf8b0af20716b81a",
sizeBytes: 332_591,
url: cameraWheelUrl,
}),
Object.freeze({
distribution: "datalab-pulse-characterization",
version: "0.2.0",
filename: "datalab_pulse_characterization-0.2.0-py3-none-any.whl",
sha256:
"317c1c1bb4dc32d270fa6f6586e830c0cea41dd3695bc70b42f4628d54466919",
sizeBytes: 82_275,
"d8797b52a5172a8c426ab72c8772001070760ee71d5a755f4ccdcd9bbc19c83e",
sizeBytes: 82_323,
url: pulseWheelUrl,
}),
]);
Expand Down
64 changes: 64 additions & 0 deletions src/runtime/dlplugins/datalab/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright (c) DataLab Platform Developers, BSD 3-Clause License
# See LICENSE file for details
"""
Portable copy of the :mod:`datalab.plugins` package for DataLab-Web.

Like DataLab Desktop, the plugin host lives in :mod:`datalab.plugins.base` and its
public names are re-exported here, while the plugin contracts (recipes, tools,
examples...) are submodules of this package.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from datalab.plugins.base import (
PLUGINS_DEFAULT_PATH,
ClassicsImageFormat,
FailedPluginInfo,
FormatInfo,
ImageFormatBase,
PluginBase,
PluginBaseMeta,
PluginCapability,
PluginInfo,
PluginRegistry,
SignalFormatBase,
discover_plugins,
format_tool_requirement,
reload_plugin_modules,
)

__all__ = [
"PLUGINS_DEFAULT_PATH",
"ClassicsImageFormat",
"FailedPluginInfo",
"FormatInfo",
"ImageFormatBase",
"PluginBase",
"PluginBaseMeta",
"PluginCapability",
"PluginInfo",
"PluginRegistry",
"SignalFormatBase",
"discover_plugins",
"format_tool_requirement",
"reload_plugin_modules",
]


# Importing the plugin host lazily keeps contract submodules lightweight
def __getattr__(name: str) -> object:
"""Return a public name of the plugin host, importing it on first access."""
if name in __all__:
# pylint: disable=import-outside-toplevel
from datalab.plugins import base

return getattr(base, name)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


def __dir__() -> list[str]:
"""Return module attributes, including lazily imported public names."""
return sorted({*globals(), *__all__})
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Copyright (c) DataLab Platform Developers, BSD 3-Clause License
# See LICENSE file for details
"""
Portable copy of :mod:`datalab.plugins` for DataLab-Web.
Portable copy of :mod:`datalab.plugins.base` for DataLab-Web.

This is a Qt-free re-implementation of the Qt plugin core. It keeps the
public surface (:class:`PluginInfo`, :class:`PluginBase`,
:class:`PluginRegistry`, :class:`FailedPluginInfo`,
:func:`discover_plugins`) bit-for-bit compatible with
``c:/Dev/DataLab/datalab/plugins.py`` so that an unmodified Qt plugin
``c:/Dev/DataLab/datalab/plugins/base.py`` so that an unmodified Qt plugin
that imports ``from datalab.plugins import PluginBase, PluginInfo`` and
that does not pull in :mod:`qtpy` itself loads as-is in the browser.

Expand Down Expand Up @@ -52,10 +52,10 @@
from datalab.config import MOD_NAME, Conf, _
from datalab.control.proxy import LocalProxy
from datalab.env import execenv
from datalab.plugin_examples import PluginExample, PluginExampleData
from datalab.plugin_instruments import PluginInstrument
from datalab.plugin_tools import PluginTool, ToolSelection, tool_accepts_selection
from datalab.recipes import RecipeDescriptor
from datalab.plugins.examples import PluginExample, PluginExampleData
from datalab.plugins.instruments import PluginInstrument
from datalab.plugins.recipes import RecipeDescriptor
from datalab.plugins.tools import PluginTool, ToolSelection, tool_accepts_selection

if TYPE_CHECKING:
from sigima.objects import ImageObj, NewImageParam, NewSignalParam, SignalObj
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import guidata.dataset as gds
from sigima.objects import ImageObj, SignalObj

from datalab.recipes import (
from datalab.plugins.recipes import (
RecipeCardinality,
RecipeDescriptor,
RecipeDiagnostic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

from sigima.objects import ImageObj, SignalObj

from datalab.plugin_resources import LOCAL_ID_PATTERN, split_package_resource
from datalab.recipes import RecipeObjectType
from datalab.plugins.recipes import RecipeObjectType
from datalab.plugins.resources import LOCAL_ID_PATTERN, split_package_resource

__all__ = ["PluginTool", "ToolSelection", "tool_accepts_selection"]

Expand All @@ -39,7 +39,7 @@ class PluginTool:

A tool is opened either by a ``launcher`` method, called without
arguments, or by an ``instrument`` method returning a
:class:`datalab.plugin_instruments.PluginInstrument`, which DataLab shows
:class:`datalab.plugins.instruments.PluginInstrument`, which DataLab shows
in a window with a live view and the instrument settings.

Args:
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/dlw_applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
import dlw_plugins
import guidata.dataset as gds
import sigima
from datalab.plugin_examples import PluginExampleData
from datalab.recipe_binding import (
from datalab.plugins.examples import PluginExampleData
from datalab.plugins.recipe_binding import (
RecipeInputIssueCode,
RecipeReadiness,
assess_recipe_inputs,
check_recipe_inputs,
find_input_issues,
is_compatible,
)
from datalab.recipes import (
from datalab.plugins.recipes import (
RECIPE_RUN_RECORD_OPTION,
RecipeCardinality,
RecipeDescriptor,
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/dlw_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* the directory discovery loop (``discover_plugins_in_dir(path)``);
* the hot-reload sequence (``reload_plugins()``).

The Qt counterpart lives in ``datalab/plugins.py``; here we only need
The Qt counterpart lives in ``datalab/plugins/base.py``; here we only need
the pieces that DataLab-Web actually uses, in a single self-contained
module so the bootstrap stays focused on the object model.
"""
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1695,7 +1695,7 @@ await micropip.install([${JSON.stringify(SIGIMA_INSTALL_SPEC)}, ${JSON.stringify
* Mirror the portable ``datalab.*`` shim into Pyodide's site-packages.
*
* Each entry in *sources* is keyed by the workspace-relative path
* (``./dlplugins/datalab/plugins.py``); we strip the leading
* (``./dlplugins/datalab/plugins/base.py``); we strip the leading
* ``./dlplugins/`` and write to ``/home/pyodide/<rest>``.
*/
private static installShim(
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/user_plugin_wheel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function buildPluginWheel(): Buffer {
import numpy as np

from datalab.plugins import PluginBase, PluginCapability, PluginInfo
from datalab.recipes import (
from datalab.plugins.recipes import (
RecipeDescriptor,
RecipeObjectOutput,
RecipeOutcome,
Expand Down
10 changes: 5 additions & 5 deletions tests/python/test_applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@
import dlw_plugins
import numpy as np
import pytest
from datalab.plugin_examples import PluginExampleData
from datalab.plugin_instruments import (
from datalab.plugins import PluginBase, PluginCapability, PluginInfo
from datalab.plugins.examples import PluginExampleData
from datalab.plugins.instruments import (
InstrumentAcquisition,
InstrumentFrame,
PluginInstrument,
)
from datalab.plugin_tools import PluginTool
from datalab.plugins import PluginBase, PluginCapability, PluginInfo
from datalab.recipes import (
from datalab.plugins.recipes import (
RECIPE_RUN_RECORD_OPTION,
RecipeCardinality,
RecipeDescriptor,
Expand All @@ -33,6 +32,7 @@
RecipeResultOutput,
RecipeValidationError,
)
from datalab.plugins.tools import PluginTool
from guidata import dataset as gds
from sigima.objects import create_image, create_signal
from sigima.objects.scalar import TableResult
Expand Down
2 changes: 1 addition & 1 deletion tests/python/test_camera_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import dlw_applications
import dlw_plugins
import pytest
from datalab.recipes import RECIPE_RUN_RECORD_OPTION
from datalab.plugins.recipes import RECIPE_RUN_RECORD_OPTION

REPO_ROOT = Path(__file__).resolve().parents[2]
CAMERA_WHEEL = (
Expand Down
2 changes: 1 addition & 1 deletion tests/python/test_pulse_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import dlw_applications
import dlw_plugins
import pytest
from datalab.recipes import RECIPE_RUN_RECORD_OPTION
from datalab.plugins.recipes import RECIPE_RUN_RECORD_OPTION

REPO_ROOT = Path(__file__).resolve().parents[2]
PULSE_WHEEL = (
Expand Down
Loading