Skip to content

Commit 6a75fa9

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

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-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 properties. They may be used in the notebook cells
40+
*/
41+
fun declareProperties(properties: Iterable<PropertyDeclaration>)
3742
}

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

+22
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,24 @@ class FieldHandlerByClass(
3637
) : FieldHandler {
3738
override fun acceptsType(type: KType) = type.isSubtypeOf(kClass.starProjectedType)
3839
}
40+
41+
data class PropertyDeclaration(
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.declareProperties(vararg properties: PropertyDeclaration) = declareProperties(properties.toList())
60+
fun KotlinKernelHost.declareProperties(vararg properties: Pair<VariableName, Any?>) = declareProperties(properties.map { PropertyDeclaration(it.first, it.second) })

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

+33
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.PropertyDeclaration
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,34 @@ 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 declareProperties(properties: Iterable<PropertyDeclaration>) {
170+
val tempDeclarations = properties.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 propertiesMap = resultClass.declaredMemberProperties.associateBy { it.name }
180+
181+
val declarations = properties.joinToString("\n") {
182+
@Suppress("UNCHECKED_CAST")
183+
val prop = propertiesMap[it.name] as KMutableProperty1<Any, Any?>
184+
prop.set(result, it.value)
185+
it.declaration
186+
}
187+
execute(declarations)
188+
}
189+
190+
companion object {
191+
private const val TEMP_OBJECT_NAME = "___temp_declarations"
192+
193+
private val PropertyDeclaration.mutabilityQualifier get() = if (isMutable) "var" else "val"
194+
private val PropertyDeclaration.declaration get() = """$mutabilityQualifier `$name`: $type = $TEMP_OBJECT_NAME.`$name` as $type"""
195+
private val PropertyDeclaration.tempDeclaration get() = """var `$name`: ${type.withNullability(true)} = null"""
196+
}
164197
}
165198
}

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

+34
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ 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.PropertyDeclaration
10+
import org.jetbrains.kotlinx.jupyter.api.declareProperties
911
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
1012
import org.jetbrains.kotlinx.jupyter.api.libraries.ResourceType
1113
import org.jetbrains.kotlinx.jupyter.config.defaultRepositories
@@ -25,6 +27,7 @@ import org.junit.jupiter.api.Test
2527
import org.junit.jupiter.api.assertDoesNotThrow
2628
import org.junit.jupiter.api.assertThrows
2729
import java.io.File
30+
import kotlin.reflect.typeOf
2831
import kotlin.test.assertEquals
2932
import kotlin.test.assertNull
3033
import kotlin.test.assertTrue
@@ -424,4 +427,35 @@ class CustomLibraryResolverTests : AbstractReplTest() {
424427
}
425428
assertEquals(LibraryProblemPart.BEFORE_CELL_CALLBACKS, e.part)
426429
}
430+
431+
@Test
432+
@ExperimentalStdlibApi
433+
fun testLibraryProperties() {
434+
val mutProp = arrayListOf(1)
435+
436+
val repl = testOneLibUsage(
437+
library {
438+
onLoaded {
439+
declareProperties(
440+
"x1" to 22,
441+
"x2" to 20
442+
)
443+
444+
declareProperties(
445+
PropertyDeclaration("x3", mutProp, typeOf<ArrayList<Int>>())
446+
)
447+
}
448+
}
449+
)
450+
451+
val result = repl.eval(
452+
"""
453+
x3.add(2)
454+
x1 + x2
455+
""".trimIndent()
456+
).resultValue
457+
458+
assertEquals(42, result)
459+
assertEquals(listOf(1, 2), mutProp)
460+
}
427461
}

0 commit comments

Comments
 (0)