Skip to content

Commit

Permalink
Shuffle code and rework sessions.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Fleming committed Nov 11, 2024
1 parent 7ce9973 commit f4eee2f
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 174 deletions.
4 changes: 2 additions & 2 deletions examples/menu.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@
"metadata": {},
"outputs": [],
"source": [
"async def show_id(active_widget: ipylab.ShellConnection):\n",
" id_ = await active_widget.get_property(\"id\")\n",
"async def show_id(current_widget: ipylab.ShellConnection):\n",
" id_ = await current_widget.get_property(\"id\")\n",
" await app.dialog.show_dialog(\"Show id\", f\"Widget id is {id_}\")\n",
"\n",
"\n",
Expand Down
5 changes: 2 additions & 3 deletions examples/plugins.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,9 @@
"Then try.\n",
"```python\n",
"\n",
"test # Should print 'Test' as defined in the 'namespace_objects' plugin.\n",
"\n",
"# Now lets load a different namespace is available.\n",
"app.activate_namespace('test')\n",
"test # Should print 'Test' as defined in the 'namespace_objects' plugin.\n",
"dir()\n",
"\n",
"# To switch back use\n",
Expand Down Expand Up @@ -269,7 +268,7 @@
"\n",
"#### Command\n",
"\n",
"The ipylab kernel can be started/re-started with the command 'Start ipylab kernel' (`Ctrl c` -> 'Start ipylab kernel').\n",
"The ipylab kernel can be started/re-started with the command 'Start ipylab kernel' (`Ctrl c` -> 'Start or restart ipylab kernel').\n",
"\n",
"#### Configure\n",
"\n",
Expand Down
54 changes: 10 additions & 44 deletions examples/sessions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,7 @@
"metadata": {},
"outputs": [],
"source": [
"app.all_sessions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Show current session"
"t = app.sessions.get_running()"
]
},
{
Expand All @@ -63,33 +56,14 @@
"metadata": {},
"outputs": [],
"source": [
"app.current_session"
"t.result()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example: Create Console with current session\n",
"The following two commands should both create a console panel sharing the same `session` as the current notebook."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"app.commands.execute(\"console:create\", app.current_session)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create a new Notebook\n",
"\n",
"Create a new notebook and use the connection to interact with the notebook directly."
"## Show current session"
]
},
{
Expand All @@ -98,7 +72,7 @@
"metadata": {},
"outputs": [],
"source": [
"t = app.commands.execute(\"notebook:create-new\", transform=ipylab.Transform.connection)"
"t = app.sessions.get_current()"
]
},
{
Expand All @@ -107,24 +81,16 @@
"metadata": {},
"outputs": [],
"source": [
"nb = t.result()\n",
"nb # A Connection to the notebook."
"session = t.result()\n",
"session"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use the connection to the notebook to do various actions. Lets list the attributes of the context."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"nb.execute_method(\"context.rename\", \"My renamed notebook.ipynb\")"
"## Example: Create Console with current session\n",
"This command creates a new console sharing the same `session` as the current notebook."
]
},
{
Expand All @@ -133,7 +99,7 @@
"metadata": {},
"outputs": [],
"source": [
"nb.close()"
"app.commands.execute(\"console:create\", session)"
]
}
],
Expand All @@ -153,7 +119,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.7"
"version": "3.11.10"
}
},
"nbformat": 4,
Expand Down
14 changes: 7 additions & 7 deletions ipylab/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,25 @@ def add(
Special args
------------
* active_widget: ShellConnection
* current_widget: ShellConnection
* ref: ShellConnection
Include in the argument list of the function to have the value provided when the command
is called.
active_widget:
This is a ShellConnection to the Jupyterlab defined active widget.
current_widget:
This is a ShellConnection to the Jupyterlab defined current widget.
For the command to appear in the context menu non-ipylab widgets, the appropriate selector
should be used. see: https://jupyterlab.readthedocs.io/en/stable/developer/css.html#commonly-used-css-selectors
Selectors:
* Notebook: '.jp-Notebook'
* Main area: '.jp-Activity'
ref:
This is a ShellConnection to the Ipylab active widget.
This is a ShellConnection to the Ipylab current widget.
The associated widget/panel is then accessible by `ref.widget`.
Tip: This is can be used in context menus to perform actions specific to the active widget
Tip: This is can be used in context menus to perform actions specific to the current widget
in the shell.
"""
cid = CommandPalletItemConnection.to_cid(command, category)
Expand Down Expand Up @@ -198,12 +198,12 @@ async def _execute_for_frontend(self, payload: dict, buffers: list):
args = conn.args | (payload.get("args") or {}) | {"buffers": buffers}

# Shell connections
cids = {"active_widget": payload["cid1"], "ref": payload["cid2"]}
cids = {"current_widget": payload["cid1"], "ref": payload["cid2"]}

glbls = ipylab.app.get_namespace(conn.namespace_name)
kwgs = {}
for n, p in inspect.signature(cmd).parameters.items():
if n in ["active_widget", "ref"] and cids[n]:
if n in ["current_widget", "ref"] and cids[n]:
kwgs[n] = ShellConnection(cids[n])
await kwgs[n].ready()
elif n in args:
Expand Down
5 changes: 5 additions & 0 deletions ipylab/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ipylab.ipylab import Ipylab

if TYPE_CHECKING:
from asyncio import Task
from collections.abc import Generator
from typing import Literal, Self, overload

Expand Down Expand Up @@ -169,3 +170,7 @@ def activate(self):
"Activate the connected widget in the shell."

return self.operation("activate")

def get_session(self) -> Task[dict]:
"""Get the session of the connected widget."""
return self.operation("getSession")
3 changes: 2 additions & 1 deletion ipylab/dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ def show_dialog(
see: https://jupyterlab.readthedocs.io/en/stable/api/functions/apputils.showDialog.html
source: https://jupyterlab.readthedocs.io/en/stable/extension/ui_helpers.html#generic-dialog
"""
kwgs["toLuminoWidget"] = ["body"] if isinstance(body, Widget) else []
if isinstance(body, Widget) and "toLuminoWidget" not in kwgs:
kwgs["toLuminoWidget"] = ["body"]
return self.operation("showDialog", _combine(options, title=title, body=body), **kwgs)

def show_error_message(
Expand Down
7 changes: 2 additions & 5 deletions ipylab/jupyterfrontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ class App(Ipylab):
_model_name = Unicode("JupyterFrontEndModel").tag(sync=True)
ipylab_base = IpylabBase(Obj.IpylabModel, "app").tag(sync=True)
version = Unicode(read_only=True).tag(sync=True)
current_widget_id = Unicode(read_only=True).tag(sync=True)
logger_level = UseEnum(LogLevel, read_only=True, default_value=LogLevel.warning).tag(sync=True)
current_session = Dict(read_only=True).tag(sync=True)
all_sessions = Tuple(read_only=True).tag(sync=True)
vpath = Unicode(read_only=True).tag(sync=True)
per_kernel_widget_manager_detected = Bool(read_only=True).tag(sync=True)

Expand Down Expand Up @@ -174,9 +171,9 @@ async def _evaluate(self, options: dict, buffers: list):
self.get_namespace(namespace_name, glbls)
return {"payload": glbls.get("payload"), "buffers": buffers}

def _context_open_console(self, ref: ShellConnection, active_widget: ShellConnection):
def _context_open_console(self, ref: ShellConnection, current_widget: ShellConnection):
"This command is provided for the 'autostart' context menu."
return self.open_console(objects={"ref": ref, "active_widget": active_widget})
return self.open_console(objects={"ref": ref, "current_widget": current_widget})

def open_console(
self,
Expand Down
18 changes: 15 additions & 3 deletions ipylab/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@

from __future__ import annotations

from typing import TYPE_CHECKING

from traitlets import Unicode

from ipylab.common import Obj
from ipylab.ipylab import Ipylab, IpylabBase

if TYPE_CHECKING:
from asyncio import Task


class SessionManager(Ipylab):
"""
Expand All @@ -14,11 +21,16 @@ class SessionManager(Ipylab):

SINGLE = True

_model_name = Unicode("SessionManagerModel", help="Name of the model.", read_only=True).tag(sync=True)
ipylab_base = IpylabBase(Obj.IpylabModel, "app.serviceManager.sessions").tag(sync=True)

def refresh_running(self):
"""Force a call to refresh running sessions."""
return self.execute_method("refreshRunning")
def get_running(self, *, refresh=True) -> Task[dict]:
"Get a dict of running sessions."
return self.operation("getRunning", {"refresh": refresh})

def get_current(self):
"Get the session of the current widget in the shell."
return self.operation("getCurrentSession")

def stop_if_needed(self, path):
"""
Expand Down
30 changes: 21 additions & 9 deletions ipylab/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from __future__ import annotations

import inspect
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Unpack

from ipywidgets import DOMWidget, TypedTuple, Widget
from traitlets import Container, Instance, Unicode

import ipylab
from ipylab import Area, InsertMode, Ipylab, ShellConnection, Transform, pack
from ipylab.common import Obj
from ipylab.common import IpylabKwgs, Obj, TaskHookType
from ipylab.ipylab import IpylabBase

if TYPE_CHECKING:
Expand Down Expand Up @@ -39,6 +39,7 @@ class Shell(Ipylab):

_model_name = Unicode("ShellModel", help="Name of the model.", read_only=True).tag(sync=True)
ipylab_base = IpylabBase(Obj.IpylabModel, "app.shell").tag(sync=True)
current_widget_id = Unicode(read_only=True).tag(sync=True)

connections: Container[tuple[ShellConnection, ...]] = TypedTuple(trait=Instance(ShellConnection))

Expand All @@ -53,10 +54,11 @@ def add(
ref: ShellConnection | None = None,
options: dict | None = None,
vpath: str | dict[Literal["title"], str] = "",
hooks: TaskHookType = None,
**args,
) -> Task[ShellConnection]:
"""
Add a widget or evaluation to the shell.
Add a widget to the shell.
obj
---
Expand All @@ -66,7 +68,8 @@ def add(
**Only relevant for 'evaluate'**
The 'virtual' path for the app. A new kernel will be created if a session
doesn't exist with the same path.
If a dict is provided, a text_dialog will be used to obtain the vpath.
If a dict is provided, a text_dialog will be used to obtain the vpath with the
hook `vpath_getter`.
Note:
The result (payload) of evaluate must be a Widget with a view and NOT a ShellConnection.
Expand All @@ -81,7 +84,7 @@ def add(
app.shell.add("ipylab.Panel([ipw.HTML('<h1>Test')])", vpath="test")
```
"""
hooks: TaskHooks = {"add_to_tuple_fwd": [(self, "connections")]}
hooks_: TaskHooks = {"add_to_tuple_fwd": [(self, "connections")]}
args["options"] = {
"activate": activate,
"mode": InsertMode(mode),
Expand All @@ -105,9 +108,9 @@ def add(
if c.widget is obj:
args["cid"] = c.cid
break
hooks["trait_add_fwd"] = [("widget", obj)]
hooks_["trait_add_fwd"] = [("widget", obj)]
if isinstance(obj, ipylab.Panel):
hooks["add_to_tuple_fwd"].append((obj, "connections"))
hooks_["add_to_tuple_fwd"].append((obj, "connections"))
args["ipy_model"] = obj.model_id
if isinstance(obj, DOMWidget):
obj.add_class(ipylab.app.selector.removeprefix("."))
Expand All @@ -124,11 +127,11 @@ async def add_to_shell() -> ShellConnection:
else:
args["vpath"] = vpath or ipylab.app.vpath
if args["vpath"] != ipylab.app.vpath:
hooks["trait_add_fwd"] = [("auto_dispose", False)]
hooks_["trait_add_fwd"] = [("auto_dispose", False)]
else:
args["vpath"] = ipylab.app.vpath

return await self.operation("addToShell", {"args": args}, transform=Transform.connection)
return await self.operation("addToShell", {"args": args}, transform=Transform.connection, hooks=hooks_)

return self.to_task(add_to_shell(), "Add to shell", hooks=hooks)

Expand All @@ -143,3 +146,12 @@ def collapse_left(self):

def collapse_right(self):
return self.execute_method("collapseRight")

def connect_to_widget(self, widget_id="", **kwgs: Unpack[IpylabKwgs]) -> Task[ShellConnection]:
"Make a connection to a widget in the shell (see also `get_widget_ids`)."
kwgs["transform"] = Transform.connection
return self.operation("getWidget", {"id": widget_id}, **kwgs)

def list_widget_ids(self, **kwgs: Unpack[IpylabKwgs]) -> Task[dict[Area, list[str]]]:
"Get a mapping of Areas to a list of widget ids in that area in the shell."
return self.operation("getWidgetIds", **kwgs)
3 changes: 2 additions & 1 deletion src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { PanelModel, PanelView } from './widgets/panel';
import { ShellModel } from './widgets/shell';
import { SplitPanelModel, SplitPanelView } from './widgets/split_panel';
import { TitleModel } from './widgets/title';

import { SessionManagerModel } from './widgets/sessions';
export {
CommandRegistryModel,
ConnectionModel,
Expand All @@ -24,6 +24,7 @@ export {
NotificationManagerModel,
PanelModel,
PanelView,
SessionManagerModel,
ShellConnectionModel,
ShellModel,
SplitPanelModel,
Expand Down
Loading

0 comments on commit f4eee2f

Please sign in to comment.