@@ -63,6 +63,8 @@ typealias Code = String
63
63
interface ReplForJupyter {
64
64
fun eval (code : Code , displayHandler : ((Any ) -> Unit )? = null, jupyterId : Int = -1): EvalResult
65
65
66
+ fun evalOnShutdown (): List <EvalResult >
67
+
66
68
fun checkComplete (code : Code ): CheckResult
67
69
68
70
suspend fun complete (code : String , cursor : Int , callback : (CompletionResult ) -> Unit )
@@ -110,6 +112,7 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
110
112
private val typeRenderers = mutableMapOf<String , String >()
111
113
112
114
private val initCellCodes = mutableListOf<String >()
115
+ private val shutdownCodes = mutableListOf<String >()
113
116
114
117
private fun renderResult (value : Any? , resultField : Pair <String , KotlinType >? ): Any? {
115
118
if (value == null || resultField == null ) return null
@@ -119,13 +122,20 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
119
122
return renderResult(result.value, result.resultField)
120
123
}
121
124
122
- data class PreprocessingResult (val code : Code , val initCodes : List <Code >, val initCellCodes : List <Code >, val typeRenderers : List <TypeHandler >)
125
+ data class PreprocessingResult (
126
+ val code : Code ,
127
+ val initCodes : List <Code >,
128
+ val shutdownCodes : List <Code >,
129
+ val initCellCodes : List <Code >,
130
+ val typeRenderers : List <TypeHandler >,
131
+ )
123
132
124
133
fun preprocessCode (code : String ): PreprocessingResult {
125
134
126
135
val processedMagics = magics.processMagics(code)
127
136
128
137
val initCodes = mutableListOf<Code >()
138
+ val shutdownCodes = mutableListOf<Code >()
129
139
val initCellCodes = mutableListOf<Code >()
130
140
val typeRenderers = mutableListOf<TypeHandler >()
131
141
val typeConverters = mutableListOf<TypeHandler >()
@@ -141,13 +151,16 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
141
151
typeRenderers.addAll(libraryDefinition.renderers)
142
152
typeConverters.addAll(libraryDefinition.converters)
143
153
annotations.addAll(libraryDefinition.annotations)
154
+ initCellCodes.addAll(libraryDefinition.initCell)
155
+ shutdownCodes.addAll(libraryDefinition.shutdown)
144
156
libraryDefinition.init .forEach {
145
157
146
158
// Library init code may contain other magics, so we process them recursively
147
159
val preprocessed = preprocessCode(it)
148
160
initCodes.addAll(preprocessed.initCodes)
149
161
typeRenderers.addAll(preprocessed.typeRenderers)
150
162
initCellCodes.addAll(preprocessed.initCellCodes)
163
+ shutdownCodes.addAll(preprocessed.shutdownCodes)
151
164
if (preprocessed.code.isNotBlank())
152
165
initCodes.add(preprocessed.code)
153
166
}
@@ -159,7 +172,7 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
159
172
initCodes.add(declarations)
160
173
}
161
174
162
- return PreprocessingResult (processedMagics.code, initCodes, initCellCodes, typeRenderers)
175
+ return PreprocessingResult (processedMagics.code, initCodes, shutdownCodes, initCellCodes, typeRenderers)
163
176
}
164
177
165
178
private val ctx = KotlinContext ()
@@ -327,6 +340,7 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
327
340
328
341
private fun registerNewLibraries (p : PreprocessingResult ) {
329
342
p.initCellCodes.filter { ! initCellCodes.contains(it) }.let (initCellCodes::addAll)
343
+ p.shutdownCodes.filter { ! shutdownCodes.contains(it) }.let (shutdownCodes::addAll)
330
344
typeRenderers.putAll(p.typeRenderers.map { it.className to it.code })
331
345
}
332
346
@@ -395,6 +409,10 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
395
409
}
396
410
}
397
411
412
+ override fun evalOnShutdown (): List <EvalResult > {
413
+ return shutdownCodes.map(::evalWithReturn)
414
+ }
415
+
398
416
private fun updateOutputList (jupyterId : Int , result : Any? ) {
399
417
if (jupyterId >= 0 ) {
400
418
while (ReplOutputs .count() <= jupyterId) ReplOutputs .add(null )
@@ -457,6 +475,17 @@ class ReplForJupyterImpl(private val scriptClasspath: List<File> = emptyList(),
457
475
processAnnotations(lastReplLine())
458
476
}
459
477
478
+ // Result of this function is considered to be used for testing/debug purposes
479
+ private fun evalWithReturn (code : String ): EvalResult {
480
+ val result = try {
481
+ doEval(code)
482
+ } catch (e: Exception ) {
483
+ InternalEvalResult (null , null )
484
+ }
485
+ processAnnotations(lastReplLine())
486
+ return EvalResult (result.value)
487
+ }
488
+
460
489
private data class InternalEvalResult (val value : Any? , val resultField : Pair <String , KotlinType >? )
461
490
462
491
private interface LockQueueArgs <T > {
0 commit comments