1
- import React , { useState } from 'react' ;
1
+ import React , { useState , useEffect } from 'react' ;
2
2
import { ISignal } from '@lumino/signaling' ;
3
3
import { ReactWidget , ISessionContext } from '@jupyterlab/apputils' ;
4
4
import { IChangedArgs } from '@jupyterlab/coreutils' ;
@@ -32,6 +32,19 @@ type Usage = {
32
32
33
33
const POLL_INTERVAL_SEC = 5 ;
34
34
35
+ type KernelChangeCallback = (
36
+ _sender : ISessionContext ,
37
+ args : IChangedArgs <
38
+ Kernel . IKernelConnection | null ,
39
+ Kernel . IKernelConnection | null ,
40
+ 'kernel'
41
+ >
42
+ ) => void ;
43
+ let kernelChangeCallback : {
44
+ callback : KernelChangeCallback ;
45
+ panel : NotebookPanel ;
46
+ } | null = null ;
47
+
35
48
const KernelUsage = ( props : {
36
49
widgetAdded : ISignal < INotebookTracker , NotebookPanel | null > ;
37
50
currentNotebookChanged : ISignal < INotebookTracker , NotebookPanel | null > ;
@@ -44,61 +57,87 @@ const KernelUsage = (props: {
44
57
45
58
useInterval ( async ( ) => {
46
59
if ( kernelId && panel . isVisible ) {
47
- requestUsage ( kernelId ) . then ( ( usage ) => setUsage ( usage ) ) ;
60
+ requestUsage ( kernelId )
61
+ . then ( usage => setUsage ( usage ) )
62
+ . catch ( ( ) => {
63
+ console . warn ( `Request failed for ${ kernelId } . Kernel restarting?` ) ;
64
+ } ) ;
48
65
}
49
66
} , POLL_INTERVAL_SEC * 1000 ) ;
50
67
51
- const requestUsage = ( kid : string ) =>
52
- requestAPI < any > ( `get_usage/${ kid } ` ) . then ( ( data ) => {
68
+ const requestUsage = ( kid : string ) => {
69
+ return requestAPI < any > ( `get_usage/${ kid } ` ) . then ( data => {
53
70
const usage : Usage = {
54
71
...data . content ,
55
72
kernelId : kid ,
56
- timestamp : new Date ( ) ,
73
+ timestamp : new Date ( )
57
74
} ;
58
75
return usage ;
59
76
} ) ;
77
+ } ;
60
78
61
- props . currentNotebookChanged . connect (
62
- ( sender : INotebookTracker , panel : NotebookPanel | null ) => {
63
- panel ?. sessionContext . kernelChanged . connect (
64
- (
65
- _sender : ISessionContext ,
66
- args : IChangedArgs <
67
- Kernel . IKernelConnection | null ,
68
- Kernel . IKernelConnection | null ,
69
- 'kernel'
70
- >
71
- ) => {
72
- /*
73
- const oldKernelId = args.oldValue?.id;
74
- if (oldKernelId) {
75
- const poll = kernelPools.get(oldKernelId);
76
- poll?.poll.dispose();
77
- kernelPools.delete(oldKernelId);
78
- }
79
- */
80
- const newKernelId = args . newValue ?. id ;
81
- if ( newKernelId ) {
82
- setKernelId ( newKernelId ) ;
83
- const path = panel ?. sessionContext . session ?. model . path ;
84
- setPath ( path ) ;
85
- requestUsage ( newKernelId ) . then ( ( usage ) => setUsage ( usage ) ) ;
86
- }
79
+ useEffect ( ( ) => {
80
+ const createKernelChangeCallback = ( panel : NotebookPanel ) => {
81
+ return (
82
+ _sender : ISessionContext ,
83
+ args : IChangedArgs <
84
+ Kernel . IKernelConnection | null ,
85
+ Kernel . IKernelConnection | null ,
86
+ 'kernel'
87
+ >
88
+ ) => {
89
+ const newKernelId = args . newValue ?. id ;
90
+ if ( newKernelId ) {
91
+ setKernelId ( newKernelId ) ;
92
+ const path = panel ?. sessionContext . session ?. model . path ;
93
+ setPath ( path ) ;
94
+ requestUsage ( newKernelId ) . then ( usage => setUsage ( usage ) ) ;
95
+ } else {
96
+ // Kernel was disposed
97
+ setKernelId ( newKernelId ) ;
87
98
}
88
- ) ;
89
- if ( panel ?. sessionContext . session ?. id !== kernelId ) {
90
- if ( panel ?. sessionContext . session ?. kernel ?. id ) {
91
- const kernelId = panel ?. sessionContext . session ?. kernel ?. id ;
92
- if ( kernelId ) {
93
- setKernelId ( kernelId ) ;
94
- const path = panel ?. sessionContext . session ?. model . path ;
95
- setPath ( path ) ;
96
- requestUsage ( kernelId ) . then ( ( usage ) => setUsage ( usage ) ) ;
97
- }
99
+ } ;
100
+ } ;
101
+
102
+ const notebookChangeCallback = (
103
+ sender : INotebookTracker ,
104
+ panel : NotebookPanel | null
105
+ ) => {
106
+ if ( panel === null ) {
107
+ // Ideally we would switch to a new "select a notebook to get kernel
108
+ // usage" screen instead of showing outdated info.
109
+ return ;
110
+ }
111
+ if ( kernelChangeCallback ) {
112
+ kernelChangeCallback . panel . sessionContext . kernelChanged . disconnect (
113
+ kernelChangeCallback . callback
114
+ ) ;
115
+ }
116
+ kernelChangeCallback = {
117
+ callback : createKernelChangeCallback ( panel ) ,
118
+ panel
119
+ } ;
120
+ panel . sessionContext . kernelChanged . connect ( kernelChangeCallback . callback ) ;
121
+
122
+ if ( panel . sessionContext . session ?. kernel ?. id !== kernelId ) {
123
+ const kernelId = panel . sessionContext . session ?. kernel ?. id ;
124
+ if ( kernelId ) {
125
+ setKernelId ( kernelId ) ;
126
+ const path = panel . sessionContext . session ?. model . path ;
127
+ setPath ( path ) ;
128
+ requestUsage ( kernelId ) . then ( usage => setUsage ( usage ) ) ;
98
129
}
99
130
}
100
- }
101
- ) ;
131
+ } ;
132
+ props . currentNotebookChanged . connect ( notebookChangeCallback ) ;
133
+ return ( ) => {
134
+ props . currentNotebookChanged . disconnect ( notebookChangeCallback ) ;
135
+ // In the ideal world we would disconnect kernelChangeCallback from
136
+ // last panel here, but this can lead to a race condition. Instead,
137
+ // we make sure there is ever only one callback active by holding
138
+ // it in a global state.
139
+ } ;
140
+ } , [ kernelId ] ) ;
102
141
103
142
if ( kernelId ) {
104
143
if ( usage ) {
0 commit comments