Skip to content

Commit e1675ba

Browse files
committed
Add support for variables declarations in library API
Fixes #206
1 parent 4e0f66c commit e1675ba

File tree

4 files changed

+100
-0
lines changed

4 files changed

+100
-0
lines changed

jupyter-lib/api/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/KotlinKernelHost.kt

+5
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,9 @@ interface KotlinKernelHost {
3434
* Adds a new library via its definition. Fully interchangeable with `%use` approach
3535
*/
3636
fun addLibrary(library: LibraryDefinition)
37+
38+
/**
39+
* Declares the given variables. They may be used in the notebook cells
40+
*/
41+
fun declareVariables(variables: Iterable<VariableDeclaration>)
3742
}

jupyter-lib/api/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/fieldsHandling.kt

+23
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.jetbrains.kotlinx.jupyter.api
33
import kotlin.reflect.KClass
44
import kotlin.reflect.KProperty
55
import kotlin.reflect.KType
6+
import kotlin.reflect.full.createType
67
import kotlin.reflect.full.isSubtypeOf
78
import kotlin.reflect.full.starProjectedType
89

@@ -36,3 +37,25 @@ class FieldHandlerByClass(
3637
) : FieldHandler {
3738
override fun acceptsType(type: KType) = type.isSubtypeOf(kClass.starProjectedType)
3839
}
40+
41+
data class VariableDeclaration(
42+
val name: VariableName,
43+
val value: Any?,
44+
val type: KType,
45+
val isMutable: Boolean = false,
46+
) {
47+
constructor(
48+
name: VariableName,
49+
value: Any?,
50+
isMutable: Boolean = false
51+
) : this(
52+
name,
53+
value,
54+
value?.let { it::class.starProjectedType } ?: Any::class.createType(nullable = true),
55+
isMutable
56+
)
57+
}
58+
59+
fun KotlinKernelHost.declareVariable(variable: VariableDeclaration) = declareVariables(listOf(variable))
60+
fun KotlinKernelHost.declareVariable(name: VariableName, value: Any?) = declareVariable(VariableDeclaration(name, value))
61+
fun KotlinKernelHost.declareVariables(variables: Map<VariableName, Any?>) = declareVariables(variables.map { VariableDeclaration(it.key, it.value) })

src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl/impl/CellExecutorImpl.kt

+35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.jetbrains.kotlinx.jupyter.api.ExecutionCallback
66
import org.jetbrains.kotlinx.jupyter.api.FieldValue
77
import org.jetbrains.kotlinx.jupyter.api.HTML
88
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost
9+
import org.jetbrains.kotlinx.jupyter.api.VariableDeclaration
910
import org.jetbrains.kotlinx.jupyter.api.libraries.CodeExecution
1011
import org.jetbrains.kotlinx.jupyter.api.libraries.ExecutionHost
1112
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
@@ -20,6 +21,9 @@ import org.jetbrains.kotlinx.jupyter.repl.CellExecutor
2021
import org.jetbrains.kotlinx.jupyter.repl.ExecutionStartedCallback
2122
import org.jetbrains.kotlinx.jupyter.repl.InternalEvalResult
2223
import java.util.LinkedList
24+
import kotlin.reflect.KMutableProperty1
25+
import kotlin.reflect.full.declaredMemberProperties
26+
import kotlin.reflect.full.withNullability
2327

2428
interface BaseKernelHost {
2529
fun <T> withHost(currentHost: KotlinKernelHost, callback: () -> T): T
@@ -161,5 +165,36 @@ internal class CellExecutorImpl(private val replContext: SharedReplContext) : Ce
161165
override fun <T> execute(callback: KotlinKernelHost.() -> T): T {
162166
return callback(ExecutionContext(sharedContext, displayHandler, executor))
163167
}
168+
169+
override fun declareVariables(variables: Iterable<VariableDeclaration>) {
170+
val tempDeclarations = variables.joinToString(
171+
"\n",
172+
"object $TEMP_OBJECT_NAME {\n",
173+
"\n}\n$TEMP_OBJECT_NAME"
174+
) {
175+
it.tempDeclaration
176+
}
177+
val result = execute(tempDeclarations).value as Any
178+
val resultClass = result::class
179+
val properties = resultClass.declaredMemberProperties.associate {
180+
@Suppress("UNCHECKED_CAST")
181+
it.name to (it as KMutableProperty1<Any, Any?>)
182+
}
183+
184+
val declarations = variables.joinToString("\n") {
185+
val prop = properties[it.name]
186+
prop?.set(result, it.value)
187+
it.declaration
188+
}
189+
execute(declarations)
190+
}
191+
192+
companion object {
193+
private const val TEMP_OBJECT_NAME = "___temp_declarations"
194+
195+
private val VariableDeclaration.mutabilityQualifier get() = if (isMutable) "var" else "val"
196+
private val VariableDeclaration.declaration get() = """$mutabilityQualifier `$name`: $type = $TEMP_OBJECT_NAME.`$name` as $type"""
197+
private val VariableDeclaration.tempDeclaration get() = """var `$name`: ${type.withNullability(true)} = null"""
198+
}
164199
}
165200
}

src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/repl/CustomLibraryResolverTests.kt

+37
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import kotlinx.serialization.SerializationException
66
import org.jetbrains.kotlinx.jupyter.ReplForJupyter
77
import org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl
88
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion.Companion.toMaybeUnspecifiedString
9+
import org.jetbrains.kotlinx.jupyter.api.VariableDeclaration
10+
import org.jetbrains.kotlinx.jupyter.api.declareVariable
11+
import org.jetbrains.kotlinx.jupyter.api.declareVariables
912
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
1013
import org.jetbrains.kotlinx.jupyter.api.libraries.ResourceType
1114
import org.jetbrains.kotlinx.jupyter.config.defaultRepositories
@@ -25,6 +28,7 @@ import org.junit.jupiter.api.Test
2528
import org.junit.jupiter.api.assertDoesNotThrow
2629
import org.junit.jupiter.api.assertThrows
2730
import java.io.File
31+
import kotlin.reflect.typeOf
2832
import kotlin.test.assertEquals
2933
import kotlin.test.assertNull
3034
import kotlin.test.assertTrue
@@ -424,4 +428,37 @@ class CustomLibraryResolverTests : AbstractReplTest() {
424428
}
425429
assertEquals(LibraryProblemPart.BEFORE_CELL_CALLBACKS, e.part)
426430
}
431+
432+
@Test
433+
@ExperimentalStdlibApi
434+
fun testLibraryProperties() {
435+
val mutProp = arrayListOf(1)
436+
437+
val repl = testOneLibUsage(
438+
library {
439+
onLoaded {
440+
declareVariables(
441+
mapOf(
442+
"x1" to 22,
443+
"x2" to 20
444+
)
445+
)
446+
447+
declareVariable(
448+
VariableDeclaration("x3", mutProp, typeOf<ArrayList<Int>>())
449+
)
450+
}
451+
}
452+
)
453+
454+
val result = repl.eval(
455+
"""
456+
x3.add(2)
457+
x1 + x2
458+
""".trimIndent()
459+
).resultValue
460+
461+
assertEquals(42, result)
462+
assertEquals(listOf(1, 2), mutProp)
463+
}
427464
}

0 commit comments

Comments
 (0)