Skip to content

Commit eef63ff

Browse files
authored
Add support for ipywidgets (#125)
* Add support for ipywidgets * Use SimplifiedOutputArea for widgets * Fix DummyHandler * Section and screenshot about ipywidgets in README.md * Move widget check to check_imported * Check isAttached before recreating the table * Add ipywidgets to Binder
1 parent bbd0af0 commit eef63ff

8 files changed

+138
-21
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ For the time being, this project is still in its first steps. Contributions in a
1414

1515
- Allows inspection of variables for both python consoles and python notebooks
1616
- Allows inspection of matrices in a datagrid-viewer. This might not work for large matrices.
17+
- Allows an inline and interactive inspection of Jupyter Widgets.
1718

1819
## Prerequisites
1920

@@ -25,6 +26,17 @@ For the time being, this project is still in its first steps. Contributions in a
2526
- `pyspark` for spark support.
2627
- `tensorflow` and `keras` to allow inspection of tf objects.
2728

29+
### Requirements for `ipywidgets` functionality
30+
31+
The variable inspector can also display Jupyter interactive widgets:
32+
33+
![ipywidgets](./ipywidgets.png)
34+
35+
The requirements for this functionality are:
36+
37+
- `ipywidgets`
38+
- Support for widgets in JupyterLab: `jupyter labextension install @jupyter-widgets/jupyterlab-manager`
39+
2840
### Requirements for R functionality
2941

3042
- The `repr` library.

binder/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: jupyterlab-variable-inspector
22
channels:
33
- conda-forge
44
dependencies:
5+
- ipywidgets=7
56
- jupyterlab=1
67
- numpy
78
- pandas

ipywidgets.png

223 KB
Loading

src/handler.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import {
2+
IRenderMimeRegistry
3+
} from '@jupyterlab/rendermime';
4+
15
import {
26
IDisposable
37
} from '@phosphor/disposable';
@@ -15,7 +19,7 @@ import {
1519
} from "@jupyterlab/apputils";
1620

1721
import {
18-
KernelMessage
22+
KernelMessage, Kernel
1923
} from "@jupyterlab/services";
2024

2125
import {
@@ -37,9 +41,11 @@ export
3741
class VariableInspectionHandler implements IDisposable, IVariableInspector.IInspectable {
3842

3943
private _connector: KernelConnector;
40-
private _queryCommand: string;
44+
private _rendermime: IRenderMimeRegistry;
4145
private _initScript: string;
46+
private _queryCommand: string;
4247
private _matrixQueryCommand: string;
48+
private _widgetQueryCommand: string;
4349
private _deleteCommand: string;
4450
private _disposed = new Signal<this, void>( this );
4551
private _inspected = new Signal<this, IVariableInspector.IVariableInspectorUpdate>( this );
@@ -49,10 +55,12 @@ export
4955

5056

5157
constructor( options: VariableInspectionHandler.IOptions ) {
52-
this._connector = options.connector;
5358
this._id = options.id;
59+
this._connector = options.connector;
60+
this._rendermime = options.rendermime;
5461
this._queryCommand = options.queryCommand;
5562
this._matrixQueryCommand = options.matrixQueryCommand;
63+
this._widgetQueryCommand = options.widgetQueryCommand;
5664
this._deleteCommand = options.deleteCommand;
5765
this._initScript = options.initScript;
5866

@@ -84,6 +92,10 @@ export
8492
get id():string{
8593
return this._id;
8694
}
95+
96+
get rendermime() {
97+
return this._rendermime;
98+
}
8799

88100
/**
89101
* A signal emitted when the handler is disposed.
@@ -120,6 +132,18 @@ export
120132
this._connector.fetch( content, this._handleQueryResponse );
121133
}
122134

135+
/**
136+
* Performs an inspection of a Jupyter Widget
137+
*/
138+
public performWidgetInspection(varName: string) {
139+
const request: KernelMessage.IExecuteRequestMsg['content'] = {
140+
code: this._widgetQueryCommand + "(" + varName + ")",
141+
stop_on_error: false,
142+
store_history: false
143+
};
144+
return this._connector.execute(request);
145+
}
146+
123147
/**
124148
* Performs an inspection of the specified matrix.
125149
*/
@@ -259,7 +283,7 @@ export
259283
switch ( msgType ) {
260284
case 'execute_input':
261285
let code = msg.content.code;
262-
if ( !( code == this._queryCommand ) && !( code == this._matrixQueryCommand ) ) {
286+
if ( !( code == this._queryCommand ) && !( code == this._matrixQueryCommand ) && !( code.startsWith(this._widgetQueryCommand) ) ) {
263287
this.performInspection();
264288
}
265289
break;
@@ -280,8 +304,10 @@ namespace VariableInspectionHandler {
280304
export
281305
interface IOptions {
282306
connector: KernelConnector;
307+
rendermime?: IRenderMimeRegistry;
283308
queryCommand: string;
284309
matrixQueryCommand: string;
310+
widgetQueryCommand: string;
285311
deleteCommand: string;
286312
initScript: string;
287313
id : string;
@@ -294,6 +320,7 @@ export
294320
private _disposed = new Signal<this,void>( this );
295321
private _inspected = new Signal<this, IVariableInspector.IVariableInspectorUpdate>( this );
296322
private _connector : KernelConnector;
323+
private _rendermime : IRenderMimeRegistry = null;
297324

298325
constructor(connector : KernelConnector) {
299326
this._connector = connector;
@@ -310,6 +337,10 @@ export
310337
get inspected() : ISignal<DummyHandler, IVariableInspector.IVariableInspectorUpdate>{
311338
return this._inspected;
312339
}
340+
341+
get rendermime(): IRenderMimeRegistry {
342+
return this._rendermime;
343+
}
313344

314345
dispose(): void {
315346
if ( this.isDisposed ) {
@@ -334,5 +365,14 @@ export
334365
return new Promise(function(resolve, reject) { reject("Cannot inspect matrices w/ the DummyHandler!") });
335366
}
336367

368+
public performWidgetInspection( varName: string ): Kernel.IShellFuture<KernelMessage.IExecuteRequestMsg, KernelMessage.IExecuteReplyMsg> {
369+
const request: KernelMessage.IExecuteRequestMsg['content'] = {
370+
code: "",
371+
stop_on_error: false,
372+
store_history: false
373+
};
374+
return this._connector.execute(request);
375+
}
376+
337377
public performDelete(varName: string){}
338378
}

src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ import {
3636
} from '@jupyterlab/notebook';
3737

3838

39-
40-
4139
namespace CommandIDs {
4240
export
4341
const open = "variableinspector:open";
@@ -144,11 +142,13 @@ const consoles: JupyterFrontEndPlugin<void> = {
144142
let initScript = result.initScript;
145143
let queryCommand = result.queryCommand;
146144
let matrixQueryCommand = result.matrixQueryCommand;
145+
let widgetQueryCommand = result.widgetQueryCommand;
147146
let deleteCommand = result.deleteCommand;
148147

149148
const options: VariableInspectionHandler.IOptions = {
150149
queryCommand: queryCommand,
151150
matrixQueryCommand: matrixQueryCommand,
151+
widgetQueryCommand,
152152
deleteCommand: deleteCommand,
153153
connector: connector,
154154
initScript: initScript,
@@ -175,6 +175,7 @@ const consoles: JupyterFrontEndPlugin<void> = {
175175
delete handlers[consolePanel.id];
176176
handler.dispose();
177177
} );
178+
178179
resolve( handler );
179180
} )
180181
} );
@@ -233,6 +234,7 @@ const notebooks: JupyterFrontEndPlugin<void> = {
233234

234235
const session = nbPanel.session;
235236
const connector = new KernelConnector( { session } );
237+
const rendermime = nbPanel.content.rendermime;
236238

237239
connector.ready.then(() => { // Create connector and init w script if it exists for kernel type.
238240
let kerneltype: string = connector.kernelType;
@@ -242,13 +244,16 @@ const notebooks: JupyterFrontEndPlugin<void> = {
242244
let initScript = result.initScript;
243245
let queryCommand = result.queryCommand;
244246
let matrixQueryCommand = result.matrixQueryCommand;
247+
let widgetQueryCommand = result.widgetQueryCommand;
245248
let deleteCommand = result.deleteCommand;
246249

247250
const options: VariableInspectionHandler.IOptions = {
248251
queryCommand: queryCommand,
249252
matrixQueryCommand: matrixQueryCommand,
253+
widgetQueryCommand,
250254
deleteCommand: deleteCommand,
251255
connector: connector,
256+
rendermime,
252257
initScript: initScript,
253258
id: session.path //Using the sessions path as an identifier for now.
254259
};

src/inspectorscripts.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace Languages {
55
initScript: string;
66
queryCommand: string;
77
matrixQueryCommand: string;
8+
widgetQueryCommand: string;
89
deleteCommand: string;
910
}
1011
}
@@ -29,10 +30,11 @@ np = None
2930
pd = None
3031
pyspark = None
3132
tf = None
33+
ipywidgets = None
3234
3335
3436
def _check_imported():
35-
global np, pd, pyspark, tf
37+
global np, pd, pyspark, tf, ipywidgets
3638
if 'numpy' in sys.modules:
3739
# don't really need the try
3840
try:
@@ -59,6 +61,12 @@ def _check_imported():
5961
except ImportError:
6062
tf = None
6163
64+
if 'ipywidgets' in sys.modules:
65+
try:
66+
import ipywidgets
67+
except ImportError:
68+
ipywidgets = None
69+
6270
6371
def _jupyterlab_variableinspector_getsizeof(x):
6472
if type(x).__name__ in ['ndarray', 'Series']:
@@ -135,6 +143,10 @@ def _jupyterlab_variableinspector_is_matrix(x):
135143
return False
136144
137145
146+
def _jupyterlab_variableinspector_is_widget(x):
147+
return ipywidgets and issubclass(x, ipywidgets.DOMWidget)
148+
149+
138150
def _jupyterlab_variableinspector_dict_list():
139151
_check_imported()
140152
def keep_cond(v):
@@ -159,13 +171,18 @@ def _jupyterlab_variableinspector_dict_list():
159171
except:
160172
return False
161173
values = _jupyterlab_variableinspector_nms.who_ls()
162-
vardic = [{'varName': _v,
163-
'varType': type(eval(_v)).__name__,
164-
'varSize': str(_jupyterlab_variableinspector_getsizeof(eval(_v))),
165-
'varShape': str(_jupyterlab_variableinspector_getshapeof(eval(_v))) if _jupyterlab_variableinspector_getshapeof(eval(_v)) else '',
166-
'varContent': str(_jupyterlab_variableinspector_getcontentof(eval(_v))),
167-
'isMatrix': _jupyterlab_variableinspector_is_matrix(eval(_v))}
168-
for _v in values if keep_cond(_v)]
174+
vardic = [
175+
{
176+
'varName': _v,
177+
'varType': type(eval(_v)).__name__,
178+
'varSize': str(_jupyterlab_variableinspector_getsizeof(eval(_v))),
179+
'varShape': str(_jupyterlab_variableinspector_getshapeof(eval(_v))) if _jupyterlab_variableinspector_getshapeof(eval(_v)) else '',
180+
'varContent': str(_jupyterlab_variableinspector_getcontentof(eval(_v))),
181+
'isMatrix': _jupyterlab_variableinspector_is_matrix(eval(_v)),
182+
'isWidget': _jupyterlab_variableinspector_is_widget(type(eval(_v)))
183+
}
184+
for _v in values if keep_cond(_v)
185+
]
169186
return json.dumps(vardic, ensure_ascii=False)
170187
171188
@@ -196,6 +213,10 @@ def _jupyterlab_variableinspector_getmatrixcontent(x, max_rows=10000):
196213
return _jupyterlab_variableinspector_getmatrixcontent(s)
197214
198215
216+
def _jupyterlab_variableinspector_displaywidget(widget):
217+
display(widget)
218+
219+
199220
def _jupyterlab_variableinspector_default(o):
200221
if isinstance(o, np.number): return int(o)
201222
raise TypeError
@@ -281,24 +302,28 @@ def _jupyterlab_variableinspector_deletevariable(x):
281302
initScript: Languages.py_script,
282303
queryCommand: "_jupyterlab_variableinspector_dict_list()",
283304
matrixQueryCommand: "_jupyterlab_variableinspector_getmatrixcontent",
305+
widgetQueryCommand: "_jupyterlab_variableinspector_displaywidget",
284306
deleteCommand: "_jupyterlab_variableinspector_deletevariable"
285307
},
286308
"python2": {
287309
initScript: Languages.py_script,
288310
queryCommand: "_jupyterlab_variableinspector_dict_list()",
289311
matrixQueryCommand: "_jupyterlab_variableinspector_getmatrixcontent",
312+
widgetQueryCommand: "_jupyterlab_variableinspector_displaywidget",
290313
deleteCommand: "_jupyterlab_variableinspector_deletevariable"
291314
},
292315
"python": {
293316
initScript: Languages.py_script,
294317
queryCommand: "_jupyterlab_variableinspector_dict_list()",
295318
matrixQueryCommand: "_jupyterlab_variableinspector_getmatrixcontent",
319+
widgetQueryCommand: "_jupyterlab_variableinspector_displaywidget",
296320
deleteCommand: "_jupyterlab_variableinspector_deletevariable"
297321
},
298322
"R": {
299323
initScript: Languages.r_script,
300324
queryCommand: ".ls.objects()",
301325
matrixQueryCommand: ".ls.objects",
326+
widgetQueryCommand: "TODO",
302327
deleteCommand: ".deleteVariable"
303328
}
304329
};

src/kernelconnector.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export
7777
}
7878

7979
return kernel.ready.then(() => {
80-
let future = kernel.requestExecute( content, false );
80+
let future = kernel.requestExecute( content );
8181

8282
future.onIOPub = ( ( msg: KernelMessage.IIOPubMessage ) => {
8383
ioCallback( msg );
@@ -86,6 +86,10 @@ export
8686
} );
8787
}
8888

89+
execute( content: KernelMessage.IExecuteRequestMsg['content']) {
90+
return this._session.kernel.requestExecute(content);
91+
}
92+
8993
}
9094

9195
export

0 commit comments

Comments
 (0)