Skip to content

Commit 3c8c263

Browse files
author
Alan Fleming
committed
Added property current_widget and method activate
1 parent f6a4637 commit 3c8c263

File tree

4 files changed

+55
-23
lines changed

4 files changed

+55
-23
lines changed

ipylab/connection.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
from __future__ import annotations
55

66
import uuid
7-
from typing import Generic, TypeVar
7+
from typing import TYPE_CHECKING, Generic, TypeVar
88

99
from ipywidgets import register
1010
from traitlets import Bool, Unicode
1111

1212
from ipylab.asyncwidget import AsyncWidgetBase
1313

14+
if TYPE_CHECKING:
15+
from asyncio import Task
16+
17+
from ipylab._compat.typing import Self
18+
1419
T = TypeVar("T", bound="Connection")
1520

1621

@@ -57,7 +62,7 @@ class Connection(AsyncWidgetBase, Generic[T]):
5762
_connections: dict[str, T] = {} # noqa RUF012
5863
_model_name = Unicode("ConnectionModel").tag(sync=True)
5964
cid = Unicode(read_only=True, help="connection id").tag(sync=True)
60-
id = Unicode(allow_none=True, read_only=True, help="id of the disposable if it has one")
65+
id = Unicode("", read_only=True, help="id of the object if it has one").tag(sync=True)
6166
_dispose = Bool(read_only=True).tag(sync=True)
6267
_basename = None
6368

@@ -80,7 +85,7 @@ def __init__(self, *, cid: str, model_id=None, id: str | None = None, **kwgs):
8085
if self._async_widget_base_init_complete:
8186
return
8287
self.set_trait("cid", cid)
83-
self.set_trait("id", id)
88+
self.set_trait("id", id or "")
8489
super().__init__(model_id=model_id, **kwgs)
8590

8691
def __str__(self):
@@ -130,6 +135,11 @@ def get_existing_connection(cls, *name_or_id: str, quiet=False):
130135
class MainAreaConnection(Connection):
131136
CID_PREFIX = "ipylab MainArea"
132137

133-
def activate(self):
138+
def activate(self) -> Task[Self]:
134139
self._check_closed()
135-
return self.app.shell.execute_method("activateById", self.id)
140+
141+
async def activate_():
142+
self.app.shell.execute_method("activateById", self.id)
143+
return self
144+
145+
return self.to_task(activate_())

ipylab/jupyterfrontend.py

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from traitlets import Dict, Instance, Tuple, Unicode
99

10+
from ipylab import MainAreaConnection
1011
from ipylab.asyncwidget import AsyncWidgetBase, Transform, pack, pack_code, register, widget_serialization
1112
from ipylab.commands import CommandRegistry
1213
from ipylab.dialog import Dialog, FileDialog
@@ -38,6 +39,12 @@ class JupyterFrontEnd(AsyncWidgetBase):
3839
shell = Instance(Shell, (), read_only=True)
3940
session_manager = Instance(SessionManager, (), read_only=True)
4041

42+
@property
43+
def current_widget(self):
44+
"""A connection to the current widget in the shell."""
45+
id_ = self.current_widget_id
46+
return MainAreaConnection(cid=MainAreaConnection.to_cid(id_), id=id_)
47+
4148
def _init_python_backend(self):
4249
"Run by the Ipylab python backend."
4350
# This is called in a separate kernel started by the JavaScript frontend

src/widgets/connection.ts

+29-13
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,44 @@ import { ObjectHash } from 'backbone';
66
import { IpylabModel } from './ipylab';
77

88
/**
9-
* Maintain a connection to any object by using a cid.
9+
* Provides a connection from any object reachable here to one or more Python backends.
10+
*
11+
* Typically the object is registered first via the method `registerConnection` with a cid
12+
* In Python The `cid` is passed when creating a new Connection.
13+
*
14+
* The object is set as the base. If the object is disposable, the ConnectionModel will
15+
* also close when the object is disposed.
1016
*/
1117
export class ConnectionModel extends IpylabModel {
1218
async initialize(attributes: ObjectHash, options: any): Promise<void> {
13-
super.initialize(attributes, {
14-
...options,
15-
base: this.getConnection(this.get('cid'))
16-
});
17-
this.base.disposed.connect(() => {
18-
if (!this.get('_dispose')) {
19-
this.close();
20-
}
21-
});
22-
this.listenTo(this, 'change:_dispose', () => this.base.dispose());
19+
let base;
20+
const cid = this.get('cid');
21+
const id = this.get('id');
22+
try {
23+
base = this.getConnection(cid, id);
24+
} catch {}
25+
super.initialize(attributes, { ...options, base });
26+
if (base) {
27+
this.base.disposed.connect(() => this.close());
28+
this.on('change:_dispose', this.dispose, this);
29+
} else {
30+
console.log(
31+
`Failed to get connection for cid='${cid}' id='${id}' so closing...`
32+
);
33+
this.close();
34+
}
2335
}
2436

2537
close(comm_closed?: boolean): Promise<void> {
2638
if (this.base?.ipylabDisposeOnClose) {
2739
delete this.base.ipylabDisposeOnClose;
28-
this.base.dispose();
40+
this.dispose();
2941
}
30-
return super.close(comm_closed);
42+
return super.close((comm_closed || this.get('_dispose')) ?? false);
43+
}
44+
45+
dispose() {
46+
this.base?.dispose();
3147
}
3248

3349
/**

src/widgets/ipylab.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,13 @@ export class IpylabModel extends WidgetModel {
5757
initialize(attributes: ObjectHash, options: any): void {
5858
super.initialize(attributes, options);
5959
this.set('kernelId', this.kernelId);
60-
this.on('msg:custom', this._onCustomMessage.bind(this));
60+
this.on('msg:custom', this._onCustomMessage, this);
6161
this.save_changes();
6262
const msg = `ipylab ${this.get('_model_name')} ready for operations`;
6363
this.send({ init: msg });
6464
onKernelLost(this.kernel, this.close, this);
6565
if (typeof options.base === 'object') {
6666
this._base = options.base;
67-
delete options.base;
6867
} else {
6968
const basename = this.get('_basename');
7069
this._base = basename
@@ -240,7 +239,7 @@ export class IpylabModel extends WidgetModel {
240239
if (value.slice(0, 10) === 'IPY_MODEL_') {
241240
const model = await unpack_models(value, this.widget_manager);
242241
if (model.model_name === IpylabModel.connection_model_name) {
243-
const widget = this.getConnection(model.cid);
242+
const widget = this.getConnection(model.get('cid'), model.get('id'));
244243
if (!(widget instanceof Widget)) {
245244
throw new Error(`Failed to get a lumio widget for: ${value}`);
246245
}
@@ -390,11 +389,11 @@ export class IpylabModel extends WidgetModel {
390389
* @param cid Get an object that has been registered as a connection.
391390
* @returns
392391
*/
393-
getConnection(cid: string): any {
392+
getConnection(cid: string, id: string | null = null): any {
394393
if (Private.connection.has(cid)) {
395394
return Private.connection.get(cid);
396395
}
397-
const obj = this._getLuminoWidgetFromShell(cid);
396+
const obj = this._getLuminoWidgetFromShell(id || cid);
398397
IpylabModel.registerConnection(obj, cid);
399398
return obj;
400399
}

0 commit comments

Comments
 (0)