From cdfe7bd256c06bcee4b8c6bbd91a743446462893 Mon Sep 17 00:00:00 2001 From: martinRenou Date: Fri, 30 Aug 2024 16:32:37 +0200 Subject: [PATCH 1/4] Some more performance improvements --- src/inspectorscripts.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/inspectorscripts.ts b/src/inspectorscripts.ts index 8c0b53c..c184a8d 100644 --- a/src/inspectorscripts.ts +++ b/src/inspectorscripts.ts @@ -101,7 +101,14 @@ def _jupyterlab_variableinspector_getshapeof(x): def _jupyterlab_variableinspector_getcontentof(x): # returns content in a friendly way for python variables # pandas and numpy - if __pd and isinstance(x, __pd.DataFrame): + if isinstance(x, (bool, str, int, float, type(None))): + content = str(x) + elif isinstance(x, (list, tuple)): + if len(x) < 10: + content = str(x) + else: + content = f"[{x[0]}, {x[1]}, {x[2]}, ..., {x[-1]}]" + elif __pd and isinstance(x, __pd.DataFrame): colnames = ', '.join(x.columns.map(str)) content = "Columns: %s" % colnames elif __pd and isinstance(x, __pd.Series): @@ -152,7 +159,7 @@ def _jupyterlab_variableinspector_dict_list(): def keep_cond(v): try: obj = eval(v) - if isinstance(obj, (bool, str, list, int, float, type(None))): + if isinstance(obj, (bool, str, list, tuple, int, float, type(None))): return True if __tf and isinstance(obj, __tf.Variable): return True From 5c907bfd3e79a653f51b7653f3a124665898c91c Mon Sep 17 00:00:00 2001 From: martinRenou Date: Mon, 2 Sep 2024 13:05:53 +0200 Subject: [PATCH 2/4] Speedup big dictionaries --- src/inspectorscripts.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/inspectorscripts.ts b/src/inspectorscripts.ts index c184a8d..27dcbb4 100644 --- a/src/inspectorscripts.ts +++ b/src/inspectorscripts.ts @@ -16,6 +16,8 @@ export abstract class Languages { static py_script = `import json import sys from importlib import __import__ +from itertools import islice +import collections from IPython import get_ipython from IPython.core.magics.namespace import NamespaceMagics @@ -108,6 +110,17 @@ def _jupyterlab_variableinspector_getcontentof(x): content = str(x) else: content = f"[{x[0]}, {x[1]}, {x[2]}, ..., {x[-1]}]" + elif isinstance(x, collections.abc.Mapping): + if len(x.keys()) < 10: + content = str(x) + else: + first_ten_keys = list(islice(x.keys(), 10)) + content = "{" + for idx, key in enumerate(first_ten_keys): + if idx > 0: + content += ", " + content += f'"{key}": {x[key]}' + content += ", ...}" elif __pd and isinstance(x, __pd.DataFrame): colnames = ', '.join(x.columns.map(str)) content = "Columns: %s" % colnames @@ -159,7 +172,7 @@ def _jupyterlab_variableinspector_dict_list(): def keep_cond(v): try: obj = eval(v) - if isinstance(obj, (bool, str, list, tuple, int, float, type(None))): + if isinstance(obj, (bool, str, list, tuple, collections.abc.Mapping, int, float, type(None))): return True if __tf and isinstance(obj, __tf.Variable): return True From dec91f5be3aae48adedac357e5d55535eb235681 Mon Sep 17 00:00:00 2001 From: martinRenou Date: Tue, 3 Sep 2024 09:57:23 +0200 Subject: [PATCH 3/4] Add settings --- package.json | 1 + ...jupyterlab-variableInspector-settings.json | 13 ++++ src/handler.ts | 40 ++++++++++++ src/index.ts | 65 +++++++++++++------ src/inspectorscripts.ts | 34 ++++++++-- src/tokens.ts | 5 ++ 6 files changed, 131 insertions(+), 27 deletions(-) create mode 100644 schema/jupyterlab-variableInspector-settings.json diff --git a/package.json b/package.json index 2b11b8d..d4de286 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "yjs": "^13.5.40" }, "jupyterlab": { + "schemaDir": "schema", "extension": true, "outputDir": "lckr_jupyterlab_variableinspector/labextension", "sharedPackages": { diff --git a/schema/jupyterlab-variableInspector-settings.json b/schema/jupyterlab-variableInspector-settings.json new file mode 100644 index 0000000..90ad7ca --- /dev/null +++ b/schema/jupyterlab-variableInspector-settings.json @@ -0,0 +1,13 @@ +{ + "title": "jupyterlab-variableInspector settings", + "description": "Settings for the jupyterlab-variableInspector extension.", + "type": "object", + "properties": { + "maxItems": { + "type": "number", + "title": "Maximum number of items", + "description": "The maximum number of items to show in lists/dicts etc", + "default": 10 + } + } +} diff --git a/src/handler.ts b/src/handler.ts index 0da7ec4..4c059a3 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -1,5 +1,7 @@ import { ISessionContext } from '@jupyterlab/apputils'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; + import { IExecuteResult } from '@jupyterlab/nbformat'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; @@ -100,8 +102,12 @@ export class VariableInspectionHandler extends AbstractHandler { private _matrixQueryCommand: string; private _widgetQueryCommand: string; private _deleteCommand: string; + private _changeSettingsCommand: + | ((settings: IVariableInspector.ISettings) => string) + | undefined; private _ready: Promise; private _id: string; + private _setting: ISettingRegistry.ISettings; constructor(options: VariableInspectionHandler.IOptions) { super(options.connector); @@ -110,11 +116,14 @@ export class VariableInspectionHandler extends AbstractHandler { this._queryCommand = options.queryCommand; this._matrixQueryCommand = options.matrixQueryCommand; this._widgetQueryCommand = options.widgetQueryCommand; + this._changeSettingsCommand = options.changeSettingsCommand; this._deleteCommand = options.deleteCommand; this._initScript = options.initScript; + this._setting = options.setting; this._ready = this._connector.ready.then(() => { this._initOnKernel().then((msg: KernelMessage.IExecuteReplyMsg) => { + this.performSettingsChange(); this._connector.iopubMessage.connect(this._queryCall); return; }); @@ -131,12 +140,20 @@ export class VariableInspectionHandler extends AbstractHandler { this._ready = kernelReady.then(() => { this._initOnKernel().then((msg: KernelMessage.IExecuteReplyMsg) => { + this.performSettingsChange(); this._connector.iopubMessage.connect(this._queryCall); this.performInspection(); }); }); }; + this._setting.changed.connect(async () => { + await this._ready; + + this.performSettingsChange(); + this.performInspection(); + }); + this._connector.kernelRestarted.connect(onKernelReset); this._connector.kernelChanged.connect(onKernelReset); } @@ -232,6 +249,27 @@ export class VariableInspectionHandler extends AbstractHandler { this._connector.fetch(content, this._handleQueryResponse); } + /** + * Send a kernel request to change settings + */ + performSettingsChange(): void { + if (!this._changeSettingsCommand) { + return; + } + + const settings: IVariableInspector.ISettings = { + maxItems: this._setting.get('maxItems').composite as number + }; + + const content: KernelMessage.IExecuteRequestMsg['content'] = { + code: this._changeSettingsCommand(settings), + stop_on_error: false, + store_history: false + }; + + this._connector.fetch(content, this._handleQueryResponse); + } + /** * Initializes the kernel by running the set up script located at _initScriptPath. */ @@ -344,9 +382,11 @@ export namespace VariableInspectionHandler { queryCommand: string; matrixQueryCommand: string; widgetQueryCommand: string; + changeSettingsCommand?(settings: IVariableInspector.ISettings): string; deleteCommand: string; initScript: string; id: string; + setting: ISettingRegistry.ISettings; } } diff --git a/src/index.ts b/src/index.ts index 0883ff2..dd42f55 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,8 @@ import { IConsoleTracker } from '@jupyterlab/console'; import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; + import { listIcon } from '@jupyterlab/ui-components'; import { DummyHandler, VariableInspectionHandler } from './handler'; @@ -30,6 +32,9 @@ namespace CommandIDs { export const open = 'variableinspector:open'; } +const SETTINGS_ID = + '@lckr/jupyterlab_variableinspector:jupyterlab-variableInspector-settings'; + /** * A service providing variable introspection. */ @@ -110,17 +115,24 @@ const variableinspector: JupyterFrontEndPlugin = { */ const consoles: JupyterFrontEndPlugin = { id: '@lckr/jupyterlab-variableinspector:consoles', - requires: [IVariableInspectorManager, IConsoleTracker, ILabShell], + requires: [ + IVariableInspectorManager, + IConsoleTracker, + ILabShell, + ISettingRegistry + ], autoStart: true, - activate: ( + activate: async ( app: JupyterFrontEnd, manager: IVariableInspectorManager, consoles: IConsoleTracker, - labShell: ILabShell - ): void => { + labShell: ILabShell, + settings: ISettingRegistry + ): Promise => { const handlers: { [id: string]: Promise; } = {}; + const setting = await settings.load(SETTINGS_ID); /** * Subscribes to the creation of new consoles. If a new notebook is created, build a new handler for the consoles. @@ -150,15 +162,18 @@ const consoles: JupyterFrontEndPlugin = { const matrixQueryCommand = result.matrixQueryCommand; const widgetQueryCommand = result.widgetQueryCommand; const deleteCommand = result.deleteCommand; + const changeSettingsCommand = result.changeSettingsCommand; const options: VariableInspectionHandler.IOptions = { - queryCommand: queryCommand, - matrixQueryCommand: matrixQueryCommand, + queryCommand, + matrixQueryCommand, widgetQueryCommand, - deleteCommand: deleteCommand, - connector: connector, - initScript: initScript, - id: session.path //Using the sessions path as an identifier for now. + deleteCommand, + connector, + initScript, + changeSettingsCommand, + id: session.path, //Using the sessions path as an identifier for now. + setting }; const handler = new VariableInspectionHandler(options); manager.addHandler(handler); @@ -222,15 +237,22 @@ const consoles: JupyterFrontEndPlugin = { */ const notebooks: JupyterFrontEndPlugin = { id: '@lckr/jupyterlab-variableinspector:notebooks', - requires: [IVariableInspectorManager, INotebookTracker, ILabShell], + requires: [ + IVariableInspectorManager, + INotebookTracker, + ILabShell, + ISettingRegistry + ], autoStart: true, - activate: ( + activate: async ( app: JupyterFrontEnd, manager: IVariableInspectorManager, notebooks: INotebookTracker, - labShell: ILabShell - ): void => { + labShell: ILabShell, + settings: ISettingRegistry + ): Promise => { const handlers: { [id: string]: Promise } = {}; + const setting = await settings.load(SETTINGS_ID); /** * Subscribes to the creation of new notebooks. If a new notebook is created, build a new handler for the notebook. @@ -256,16 +278,19 @@ const notebooks: JupyterFrontEndPlugin = { const matrixQueryCommand = result.matrixQueryCommand; const widgetQueryCommand = result.widgetQueryCommand; const deleteCommand = result.deleteCommand; + const changeSettingsCommand = result.changeSettingsCommand; const options: VariableInspectionHandler.IOptions = { - queryCommand: queryCommand, - matrixQueryCommand: matrixQueryCommand, + queryCommand, + matrixQueryCommand, widgetQueryCommand, - deleteCommand: deleteCommand, - connector: connector, + deleteCommand, + connector, rendermime, - initScript: initScript, - id: session.path //Using the sessions path as an identifier for now. + initScript, + changeSettingsCommand, + id: session.path, //Using the sessions path as an identifier for now. + setting }; const handler = new VariableInspectionHandler(options); manager.addHandler(handler); diff --git a/src/inspectorscripts.ts b/src/inspectorscripts.ts index 27dcbb4..30e770c 100644 --- a/src/inspectorscripts.ts +++ b/src/inspectorscripts.ts @@ -1,3 +1,5 @@ +import { IVariableInspector } from './tokens'; + export namespace Languages { export type LanguageModel = { initScript: string; @@ -5,6 +7,7 @@ export namespace Languages { matrixQueryCommand: string; widgetQueryCommand: string; deleteCommand: string; + changeSettingsCommand?: (settings: IVariableInspector.ISettings) => string; }; } @@ -26,6 +29,8 @@ _jupyterlab_variableinspector_nms = NamespaceMagics() _jupyterlab_variableinspector_Jupyter = get_ipython() _jupyterlab_variableinspector_nms.shell = _jupyterlab_variableinspector_Jupyter.kernel.shell +_jupyterlab_variableinspector_maxitems = 10 + __np = None __pd = None __pyspark = None @@ -56,6 +61,12 @@ def _check_imported(): __xr = _attempt_import('xarray') +def _jupyterlab_variableinspector_changesettings(maxitems, **kwargs): + global _jupyterlab_variableinspector_maxitems + + _jupyterlab_variableinspector_maxitems = maxitems + + def _jupyterlab_variableinspector_getsizeof(x): if type(x).__name__ in ['ndarray', 'Series']: return x.nbytes @@ -106,15 +117,18 @@ def _jupyterlab_variableinspector_getcontentof(x): if isinstance(x, (bool, str, int, float, type(None))): content = str(x) elif isinstance(x, (list, tuple)): - if len(x) < 10: + if len(x) <= _jupyterlab_variableinspector_maxitems: content = str(x) else: - content = f"[{x[0]}, {x[1]}, {x[2]}, ..., {x[-1]}]" + content = "[" + for i in range(_jupyterlab_variableinspector_maxitems): + content += f"{x[i]}, " + content += "...]" elif isinstance(x, collections.abc.Mapping): - if len(x.keys()) < 10: + if len(x.keys()) <= _jupyterlab_variableinspector_maxitems: content = str(x) else: - first_ten_keys = list(islice(x.keys(), 10)) + first_ten_keys = list(islice(x.keys(), _jupyterlab_variableinspector_maxitems)) content = "{" for idx, key in enumerate(first_ten_keys): if idx > 0: @@ -331,21 +345,27 @@ def _jupyterlab_variableinspector_deletevariable(x): queryCommand: '_jupyterlab_variableinspector_dict_list()', matrixQueryCommand: '_jupyterlab_variableinspector_getmatrixcontent', widgetQueryCommand: '_jupyterlab_variableinspector_displaywidget', - deleteCommand: '_jupyterlab_variableinspector_deletevariable' + deleteCommand: '_jupyterlab_variableinspector_deletevariable', + changeSettingsCommand: (settings: IVariableInspector.ISettings) => + `_jupyterlab_variableinspector_changesettings(maxitems=${settings.maxItems})` }, python2: { initScript: Languages.py_script, queryCommand: '_jupyterlab_variableinspector_dict_list()', matrixQueryCommand: '_jupyterlab_variableinspector_getmatrixcontent', widgetQueryCommand: '_jupyterlab_variableinspector_displaywidget', - deleteCommand: '_jupyterlab_variableinspector_deletevariable' + deleteCommand: '_jupyterlab_variableinspector_deletevariable', + changeSettingsCommand: (settings: IVariableInspector.ISettings) => + `_jupyterlab_variableinspector_changesettings(maxitems=${settings.maxItems})` }, python: { initScript: Languages.py_script, queryCommand: '_jupyterlab_variableinspector_dict_list()', matrixQueryCommand: '_jupyterlab_variableinspector_getmatrixcontent', widgetQueryCommand: '_jupyterlab_variableinspector_displaywidget', - deleteCommand: '_jupyterlab_variableinspector_deletevariable' + deleteCommand: '_jupyterlab_variableinspector_deletevariable', + changeSettingsCommand: (settings: IVariableInspector.ISettings) => + `_jupyterlab_variableinspector_changesettings(maxitems=${settings.maxItems})` }, R: { initScript: Languages.r_script, diff --git a/src/tokens.ts b/src/tokens.ts index 118fa16..cbed401 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -53,6 +53,10 @@ export namespace IVariableInspector { performDelete(varName: string): void; } + export interface ISettings { + maxItems: number; + } + export interface IVariableInspectorUpdate { title: IVariableTitle; payload: Array; @@ -67,6 +71,7 @@ export namespace IVariableInspector { isMatrix: boolean; isWidget: boolean; } + export interface IVariableTitle { kernelName?: string; contextName?: string; //Context currently reserved for special information. From 597f7c1fba586e2a30619156d9d9d19dbc659273 Mon Sep 17 00:00:00 2001 From: martinRenou Date: Tue, 3 Sep 2024 11:08:55 +0200 Subject: [PATCH 4/4] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MichaƂ Krassowski <5832902+krassowski@users.noreply.github.com> --- schema/jupyterlab-variableInspector-settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/schema/jupyterlab-variableInspector-settings.json b/schema/jupyterlab-variableInspector-settings.json index 90ad7ca..24dde85 100644 --- a/schema/jupyterlab-variableInspector-settings.json +++ b/schema/jupyterlab-variableInspector-settings.json @@ -1,10 +1,12 @@ { - "title": "jupyterlab-variableInspector settings", + "jupyter.lab.setting-icon": "ui-components:list", + "title": "Variable Inspector", "description": "Settings for the jupyterlab-variableInspector extension.", "type": "object", "properties": { "maxItems": { "type": "number", + "minimum": 0, "title": "Maximum number of items", "description": "The maximum number of items to show in lists/dicts etc", "default": 10