From b592a1bdfb4b7441723bbe92034ef2f48fbaa6bc Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Tue, 19 Sep 2023 17:23:55 -0500 Subject: [PATCH 1/4] remove iframe --- deephaven_ipywidgets/deephaven.py | 17 ++++++++++++++++- package.json | 2 +- src/widget.ts | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/deephaven_ipywidgets/deephaven.py b/deephaven_ipywidgets/deephaven.py index 9fe251f..4cae422 100644 --- a/deephaven_ipywidgets/deephaven.py +++ b/deephaven_ipywidgets/deephaven.py @@ -7,15 +7,17 @@ """ Module for displaying Deephaven widgets within interactive python environments. """ +from __future__ import annotations import __main__ from ipywidgets import DOMWidget -from traitlets import Unicode, Integer, Bytes +from traitlets import Unicode, Integer, Bytes, Bool from deephaven_server import Server from uuid import uuid4 from ._frontend import module_name, module_version import os import base64 +import atexit def _str_object_type(obj): @@ -34,6 +36,15 @@ def _path_for_object(obj): raise TypeError(f"Unknown object type: {name}") +def _cleanup(widget: DeephavenWidget): + """ + Remove the widget when the kernel is shutdown + + Args: + widget (DeephavenWidget): The widget to remove + """ + widget.set_trait('kernel_active', False) + class DeephavenWidget(DOMWidget): """A wrapper for viewing DeephavenWidgets in IPython @@ -52,6 +63,7 @@ class DeephavenWidget(DOMWidget): width = Integer().tag(sync=True) height = Integer().tag(sync=True) token = Unicode().tag(sync=True) + kernel_active = Bool().tag(sync=True) def __init__(self, deephaven_object, height=600, width=0): """Create a Deephaven widget for displaying in an interactive Python console. @@ -122,3 +134,6 @@ def __init__(self, deephaven_object, height=600, width=0): self.set_trait('width', width) self.set_trait('height', height) self.set_trait('token', token) + self.set_trait('kernel_active', True) + + atexit.register(_cleanup, self) diff --git a/package.json b/package.json index 6a04941..748721b 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@babel/preset-env": "^7.5.0", "@deephaven/eslint-config": "^0.41.0", "@deephaven/prettier-config": "^0.41.0", - "@jupyterlab/builder": "^3.6.4", + "@jupyterlab/builder": "^4.0.5", "@phosphor/application": "^1.6.0", "@phosphor/widgets": "^1.6.0", "@types/jest": "^26.0.0", diff --git a/src/widget.ts b/src/widget.ts index e40ad87..f37f6e5 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -104,14 +104,33 @@ export class DeephavenView extends DOMWidgetView { } }; + //@ts-ignore + onDisconnect = (type): void => { + log.info(`Kernel ${type}, removing iframe`); + this.iframe.remove(); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any render(): any { const iframeUrl = this.model.get('iframe_url'); const width = this.model.get('width'); const height = this.model.get('height'); + let removed = false; window.addEventListener('message', this.handleAuthentication); + //@ts-ignore + const context = this.model.widget_manager._context; + + this.model.on('change:kernel_active', () => this.onDisconnect("exiting"), this) + + context.sessionContext.statusChanged.connect((sender: any, args: string) => { + if (args === "restarting" || args === "terminating" && !removed) { + this.onDisconnect(args); + removed = true; + } + }) + log.info('init_element for widget', iframeUrl, width, height); this.iframe = document.createElement('iframe'); From 56c507af46f543f148ae7fb98506458ca46adc35 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Wed, 20 Sep 2023 14:58:04 -0500 Subject: [PATCH 2/4] adding copy fix and listener fixes --- src/widget.ts | 60 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/widget.ts b/src/widget.ts index f37f6e5..5ea3a08 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -56,6 +56,33 @@ export class DeephavenModel extends DOMWidgetModel { export class DeephavenView extends DOMWidgetView { private iframe: HTMLIFrameElement; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private context: any; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(options: any) { + super(options); + + this.model.on( + 'change:kernel_active', + () => this.onDisconnect('exiting'), + this + ); + + // This uses the private _context property, but it's the only way to get + // the session context. These context listeners are used to listen to + // kernel events. See this file for examples within jupyter kernel: + // https://github.com/jupyter-widgets/ipywidgets/blob/47058a373d2c2b3acf101677b2745e14b76dd74b/python/jupyterlab_widgets/src/manager.ts#L427 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line no-underscore-dangle + this.context = this.model.widget_manager._context; + this.context.sessionContext.statusChanged.connect( + this.onRestartOrTerminate, + this + ); + } + sendAuthenticationResponse = ( messageId: string, childWindow: WindowProxy, @@ -104,36 +131,37 @@ export class DeephavenView extends DOMWidgetView { } }; - //@ts-ignore - onDisconnect = (type): void => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onRestartOrTerminate = (sender: any, args: string) => { + if (args === 'restarting' || args === 'terminating') { + this.onDisconnect(args); + } + }; + + onDisconnect = (type: string): void => { log.info(`Kernel ${type}, removing iframe`); this.iframe.remove(); - } + + this.model.off('change:kernel_active'); + this.context.sessionContext.statusChanged.disconnect( + this.onRestartOrTerminate, + this + ); + }; // eslint-disable-next-line @typescript-eslint/no-explicit-any render(): any { const iframeUrl = this.model.get('iframe_url'); const width = this.model.get('width'); const height = this.model.get('height'); - let removed = false; window.addEventListener('message', this.handleAuthentication); - //@ts-ignore - const context = this.model.widget_manager._context; - - this.model.on('change:kernel_active', () => this.onDisconnect("exiting"), this) - - context.sessionContext.statusChanged.connect((sender: any, args: string) => { - if (args === "restarting" || args === "terminating" && !removed) { - this.onDisconnect(args); - removed = true; - } - }) - log.info('init_element for widget', iframeUrl, width, height); this.iframe = document.createElement('iframe'); + this.iframe.allow = 'clipboard-write'; + this.iframe.src = iframeUrl; if (width > 0) { this.iframe.width = width; From bdbe919f23d495af6d81f0043ffe83f9ad0f79d9 Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Wed, 20 Sep 2023 15:12:19 -0500 Subject: [PATCH 3/4] revert version change --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 748721b..86cda75 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@babel/preset-env": "^7.5.0", "@deephaven/eslint-config": "^0.41.0", "@deephaven/prettier-config": "^0.41.0", - "@jupyterlab/builder": "^4.0.5", + "@jupyterlab/builder": "^3.6.5", "@phosphor/application": "^1.6.0", "@phosphor/widgets": "^1.6.0", "@types/jest": "^26.0.0", From 6d75461388fac77e0673e9dae452ad6768a9d78a Mon Sep 17 00:00:00 2001 From: Joe Numainville Date: Wed, 20 Sep 2023 15:56:14 -0500 Subject: [PATCH 4/4] fixing type --- src/widget.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/widget.ts b/src/widget.ts index 5ea3a08..beba07e 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -131,8 +131,7 @@ export class DeephavenView extends DOMWidgetView { } }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onRestartOrTerminate = (sender: any, args: string) => { + onRestartOrTerminate = (sender: unknown, args: string) => { if (args === 'restarting' || args === 'terminating') { this.onDisconnect(args); }