diff --git a/ipylab/ipylab.py b/ipylab/ipylab.py index 952e4ec..7a72ba6 100644 --- a/ipylab/ipylab.py +++ b/ipylab/ipylab.py @@ -333,11 +333,12 @@ def _task_done_callback(self, task: Task): # Unfortunately it looks like Javascript Promises can't be cancelled. # https://stackoverflow.com/questions/30233302/promise-is-it-possible-to-force-cancel-a-promise#30235261 - def _on_custom_msg(self, _, msg: str, buffers: list): - if not isinstance(msg, str): + def _on_custom_msg(self, _, msg: dict, buffers: list): + content = msg.get("ipylab") + if not content: return try: - c = json.loads(msg) + c = json.loads(content) if "ipylab_PY" in c: error = self._to_frontend_error(c) if "error" in c else None self._pending_operations.pop(c["ipylab_PY"]).set(c.get("payload"), error) @@ -358,7 +359,7 @@ def _to_frontend_error(self, content): return IpylabFrontendError(msg) return IpylabFrontendError(error) - async def _do_operation_for_fe(self, ipylab_FE: str, operation: str, payload: dict, buffers: list): + async def _do_operation_for_fe(self, ipylab_FE: str, operation: str, payload: dict, buffers: list | None): """Handle operation requests from the frontend and reply with a result.""" content: dict[str, Any] = {"ipylab_FE": ipylab_FE} buffers = [] @@ -373,7 +374,7 @@ async def _do_operation_for_fe(self, ipylab_FE: str, operation: str, payload: di except Exception: self.log.exception("Operation for frontend error", obj={"operation": operation, "payload": payload}) finally: - self.send(content, buffers) + self._ipylab_send(content, buffers) async def _do_operation_for_frontend(self, operation: str, payload: dict, buffers: list): """Perform an operation for a custom message with an ipylab_FE uuid.""" @@ -384,7 +385,7 @@ def _obj_operation(self, base: Obj, subpath: str, operation: str, kwgs, kwargs: return self.operation("genericOperation", kwgs, **kwargs) def close(self): - self.send({"close": True}) + self._ipylab_send({"close": True}) super().close() def ensure_run(self, aw: Callable | Awaitable | None) -> None: @@ -439,9 +440,9 @@ def add_as_trait(self, obj: HasTraits, name: str): # see: _observe_comm for removal self._has_attrs_mappings.add((obj, name)) - def send(self, content, buffers=None): + def _ipylab_send(self, content, buffers: list | None = None): try: - super().send(json.dumps(content, default=pack), buffers) + self.send({"ipylab": json.dumps(content, default=pack)}, buffers) except Exception: self.log.exception("Send error", obj=content) raise @@ -517,7 +518,7 @@ def operation( self._pending_operations[ipylab_PY] = response = Response() async def _operation(content: dict): - self.send(content) + self._ipylab_send(content) payload = await response.wait() return Transform.transform_payload(content["transform"], payload) diff --git a/src/widgets/ipylab.ts b/src/widgets/ipylab.ts index f426d3b..6f1f0ab 100644 --- a/src/widgets/ipylab.ts +++ b/src/widgets/ipylab.ts @@ -88,7 +88,7 @@ export class IpylabModel extends DOMWidgetModel { [base, subpath] = this.toBaseAndSubpath(this.get('ipylab_base'), 'this'); base = getNestedProperty({ obj: base, subpath, nullIfMissing: true }); if (!base) { - this.send({ + this.ipylabSend({ error: `Invalid ipylab_base '${this.get('ipylab_base')}'!` }); this.close(); @@ -120,7 +120,7 @@ export class IpylabModel extends DOMWidgetModel { this._pendingOperations.clear(); comm_closed = comm_closed || !this.commAvailable; if (!comm_closed) { - this.send({ closed: true }); + this.ipylabSend({ closed: true }); } Object.defineProperty(this, 'base', { value: null }); return super.close(true); @@ -135,7 +135,7 @@ export class IpylabModel extends DOMWidgetModel { /** * Send a custom msg over the comm. */ - send( + ipylabSend( content: any, callbacks?: ICallbacks, buffers?: ArrayBuffer[] | ArrayBufferView[] @@ -150,7 +150,7 @@ export class IpylabModel extends DOMWidgetModel { } content = toJSONsubstituteCylic(content); } - super.send(content, callbacks, buffers); + this.send({ ipylab: content }, callbacks, buffers); } /** @@ -171,7 +171,7 @@ export class IpylabModel extends DOMWidgetModel { // with the key `ipylab_FE`. const opDone = new PromiseDelegate(); this._pendingOperations.set(ipylab_FE, opDone); - this.send({ ipylab_FE, operation, payload }); + this.ipylabSend({ ipylab_FE, operation, payload }); const result: any = await opDone.promise; return await this.transformObject(result, transform); } @@ -217,6 +217,12 @@ export class IpylabModel extends DOMWidgetModel { } } + protected onCustomMessage(msg: any) { + if (msg.ipylab) { + this._onBackendMessage(JSON.parse(msg.ipylab)); + } + } + /** * Handle messages * 1. Response to requested operation sent to Python (ipylab_FE). @@ -226,8 +232,7 @@ export class IpylabModel extends DOMWidgetModel { * * @param msg */ - protected async onCustomMessage(msg: any) { - const content = typeof msg === 'string' ? JSON.parse(msg) : msg; + private async _onBackendMessage(content: any) { if (content.ipylab_FE) { // Result of an operation request sent to Python. const op = this._pendingOperations.get(content.ipylab_FE); @@ -268,9 +273,13 @@ export class IpylabModel extends DOMWidgetModel { obj = obj.payload; } const payload = (await this.transformObject(obj, transform)) ?? null; - this.send({ ipylab_PY, operation, payload }, null, buffers); + this.ipylabSend({ ipylab_PY, operation, payload }, null, buffers); } catch (e) { - this.send({ operation, ipylab_PY, error: `${(e as Error).message}` }); + this.ipylabSend({ + operation, + ipylab_PY, + error: `${(e as Error).message}` + }); console.error(e); } } diff --git a/src/widgets/simple_output.ts b/src/widgets/simple_output.ts index a065715..aca75a3 100644 --- a/src/widgets/simple_output.ts +++ b/src/widgets/simple_output.ts @@ -73,11 +73,10 @@ export class SimpleOutputModel extends IpylabModel { } async onCustomMessage(msg: any) { - const content = typeof msg === 'string' ? JSON.parse(msg) : msg; - if ('add' in content) { - this.add(content.add, content.clear); + if (msg.add) { + this.add(msg.add, msg.clear); } else { - await super.onCustomMessage(content); + super.onCustomMessage(msg); } }