Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 343c93d

Browse files
committedApr 6, 2021
Allow value rendering using renderers processor from API
1 parent 8e3677d commit 343c93d

File tree

11 files changed

+108
-24
lines changed

11 files changed

+108
-24
lines changed
 

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

+6
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,10 @@ interface Notebook {
6363
* Current JRE info
6464
*/
6565
val jreInfo: JREInfoProvider
66+
67+
/**
68+
* Renderers processor gives an ability to render values and
69+
* and add new renderers
70+
*/
71+
val renderersProcessor: TypeRenderersProcessor
6672
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.jetbrains.kotlinx.jupyter.api
2+
3+
import org.jetbrains.kotlinx.jupyter.api.libraries.ExecutionHost
4+
5+
/**
6+
* [TypeRenderersProcessor] is responsible for rendering objects.
7+
* You may use it to render values exactly like notebook renders results,
8+
* and also register new renderers in runtime.
9+
*/
10+
interface TypeRenderersProcessor {
11+
/**
12+
* Renders [value] in context of this execution [host]
13+
*/
14+
fun renderValue(host: ExecutionHost, value: Any?): Any?
15+
16+
/**
17+
* Adds new [renderer] for this notebook.
18+
* Don't turn on the optimizations for [PrecompiledRendererTypeHandler]
19+
*/
20+
fun registerWithoutOptimizing(renderer: RendererTypeHandler)
21+
}

‎jupyter-lib/api/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/libraries/JupyterIntegration.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,14 @@ abstract class JupyterIntegration : LibraryDefinitionProducer {
7171
}
7272

7373
inline fun <reified T : Any> render(noinline renderer: CodeCell.(T) -> Any) {
74-
val execution = ResultHandlerExecution { _, property ->
74+
return renderWithHost { _, value: T -> renderer(this, value) }
75+
}
76+
77+
inline fun <reified T : Any> renderWithHost(noinline renderer: CodeCell.(ExecutionHost, T) -> Any) {
78+
val execution = ResultHandlerExecution { host, property ->
7579
val currentCell = notebook.currentCell
7680
?: throw IllegalStateException("Current cell should not be null on renderer invocation")
77-
FieldValue(renderer(currentCell, property.value as T), property.name)
81+
FieldValue(renderer(currentCell, host, property.value as T), property.name)
7882
}
7983
addRenderer(SubtypeRendererTypeHandler(T::class, execution))
8084
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.jetbrains.kotlinx.jupyter.codegen
2+
3+
import org.jetbrains.kotlinx.jupyter.api.Code
4+
import org.jetbrains.kotlinx.jupyter.api.FieldValue
5+
import org.jetbrains.kotlinx.jupyter.api.PrecompiledRendererTypeHandler
6+
import org.jetbrains.kotlinx.jupyter.api.RendererTypeHandler
7+
import org.jetbrains.kotlinx.jupyter.api.TypeRenderersProcessor
8+
import org.jetbrains.kotlinx.jupyter.api.libraries.ExecutionHost
9+
10+
interface ResultsTypeRenderersProcessor : TypeRenderersProcessor {
11+
/**
12+
* Renders cell result [field] represented as [FieldValue] in the [host] context
13+
*/
14+
fun renderResult(host: ExecutionHost, field: FieldValue): Any?
15+
16+
/**
17+
* Adds new [renderer] for this notebook.
18+
* Returns code to be executed on execution host
19+
* for [PrecompiledRendererTypeHandler]'s.
20+
*/
21+
fun register(renderer: RendererTypeHandler): Code?
22+
}

‎jupyter-lib/shared-compiler/src/main/kotlin/org/jetbrains/kotlinx/jupyter/codegen/TypeRenderersProcessor.kt

-13
This file was deleted.

‎src/main/kotlin/org/jetbrains/kotlinx/jupyter/apiImpl.kt

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.jetbrains.kotlinx.jupyter.api.DisplayResultWithCell
88
import org.jetbrains.kotlinx.jupyter.api.JREInfoProvider
99
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion
1010
import org.jetbrains.kotlinx.jupyter.api.Notebook
11+
import org.jetbrains.kotlinx.jupyter.api.TypeRenderersProcessor
1112
import java.lang.IllegalStateException
1213

1314
class DisplayResultWrapper private constructor(
@@ -98,6 +99,7 @@ class NotebookImpl(
9899
private val runtimeProperties: ReplRuntimeProperties,
99100
) : Notebook {
100101
private val cells = hashMapOf<Int, CodeCellImpl>()
102+
internal var typeRenderersProcessor: TypeRenderersProcessor? = null
101103

102104
override val cellsList: Collection<CodeCellImpl>
103105
get() = cells.values
@@ -156,4 +158,7 @@ class NotebookImpl(
156158

157159
override val lastCell: CodeCellImpl?
158160
get() = history(1)
161+
162+
override val renderersProcessor: TypeRenderersProcessor
163+
get() = typeRenderersProcessor ?: throw IllegalStateException("Type renderers processor is not initialized yet")
159164
}

‎src/main/kotlin/org/jetbrains/kotlinx/jupyter/codegen/TypeRenderersProcessorImpl.kt

+14-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import org.jetbrains.kotlinx.jupyter.repl.ContextUpdater
1111

1212
class TypeRenderersProcessorImpl(
1313
private val contextUpdater: ContextUpdater,
14-
) : TypeRenderersProcessor {
14+
) : ResultsTypeRenderersProcessor {
1515
private var counter = 0
1616
private val typeRenderers: MutableList<HandlerWithInfo> = mutableListOf()
1717

@@ -35,8 +35,20 @@ class TypeRenderersProcessorImpl(
3535
}
3636
}
3737

38+
override fun renderValue(host: ExecutionHost, value: Any?): Any? {
39+
return renderResult(host, FieldValue(value, null))
40+
}
41+
3842
override fun register(renderer: RendererTypeHandler): Code? {
39-
if (renderer !is PrecompiledRendererTypeHandler || !renderer.mayBePrecompiled) {
43+
return register(renderer, true)
44+
}
45+
46+
override fun registerWithoutOptimizing(renderer: RendererTypeHandler) {
47+
register(renderer, false)
48+
}
49+
50+
private fun register(renderer: RendererTypeHandler, doOptimization: Boolean): Code? {
51+
if (!doOptimization || renderer !is PrecompiledRendererTypeHandler || !renderer.mayBePrecompiled) {
4052
typeRenderers.add(HandlerWithInfo(renderer, null))
4153
return null
4254
}

‎src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl.kt

+8-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import org.jetbrains.kotlinx.jupyter.codegen.FieldsProcessor
1717
import org.jetbrains.kotlinx.jupyter.codegen.FieldsProcessorImpl
1818
import org.jetbrains.kotlinx.jupyter.codegen.FileAnnotationsProcessor
1919
import org.jetbrains.kotlinx.jupyter.codegen.FileAnnotationsProcessorImpl
20-
import org.jetbrains.kotlinx.jupyter.codegen.TypeRenderersProcessor
20+
import org.jetbrains.kotlinx.jupyter.codegen.ResultsTypeRenderersProcessor
2121
import org.jetbrains.kotlinx.jupyter.codegen.TypeRenderersProcessorImpl
2222
import org.jetbrains.kotlinx.jupyter.common.looksLikeReplCommand
2323
import org.jetbrains.kotlinx.jupyter.compiler.CompilerArgsConfigurator
@@ -307,7 +307,11 @@ class ReplForJupyterImpl(
307307
executedCodeLogging != ExecutedCodeLogging.Off
308308
)
309309

310-
private val typeRenderersProcessor: TypeRenderersProcessor = TypeRenderersProcessorImpl(contextUpdater)
310+
private val typeRenderersProcessor: ResultsTypeRenderersProcessor = run {
311+
val processor = TypeRenderersProcessorImpl(contextUpdater)
312+
notebook.typeRenderersProcessor = processor
313+
processor
314+
}
311315

312316
private val fieldsProcessor: FieldsProcessor = FieldsProcessorImpl(contextUpdater)
313317

@@ -421,11 +425,11 @@ class ReplForJupyterImpl(
421425
currentClasspath.addAll(newClasspath)
422426
if (trackClasspath) {
423427
val sb = StringBuilder()
424-
if (newClasspath.count() > 0) {
428+
if (newClasspath.isNotEmpty()) {
425429
sb.appendLine("${newClasspath.count()} new paths were added to classpath:")
426430
newClasspath.sortedBy { it }.forEach { sb.appendLine(it) }
427431
}
428-
if (oldClasspath.count() > 0) {
432+
if (oldClasspath.isNotEmpty()) {
429433
sb.appendLine("${oldClasspath.count()} resolved paths were already in classpath:")
430434
oldClasspath.sortedBy { it }.forEach { sb.appendLine(it) }
431435
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import org.jetbrains.kotlinx.jupyter.api.Notebook
66
import org.jetbrains.kotlinx.jupyter.codegen.ClassAnnotationsProcessor
77
import org.jetbrains.kotlinx.jupyter.codegen.FieldsProcessor
88
import org.jetbrains.kotlinx.jupyter.codegen.FileAnnotationsProcessor
9-
import org.jetbrains.kotlinx.jupyter.codegen.TypeRenderersProcessor
9+
import org.jetbrains.kotlinx.jupyter.codegen.ResultsTypeRenderersProcessor
1010
import org.jetbrains.kotlinx.jupyter.libraries.LibrariesScanner
1111
import org.jetbrains.kotlinx.jupyter.libraries.LibraryResourcesProcessor
1212
import org.jetbrains.kotlinx.jupyter.magics.MagicsProcessor
@@ -16,7 +16,7 @@ internal data class SharedReplContext(
1616
val classAnnotationsProcessor: ClassAnnotationsProcessor,
1717
val fileAnnotationsProcessor: FileAnnotationsProcessor,
1818
val fieldsProcessor: FieldsProcessor,
19-
val typeRenderersProcessor: TypeRenderersProcessor,
19+
val typeRenderersProcessor: ResultsTypeRenderersProcessor,
2020
val magicsProcessor: MagicsProcessor,
2121
val resourcesProcessor: LibraryResourcesProcessor,
2222
val librariesScanner: LibrariesScanner,

‎src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/repl/IntegrationApiTests.kt

+19
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,23 @@ class IntegrationApiTests {
122122
assertEquals("1. 420", repl.eval("42.1").resultValue)
123123
assertEquals("2. 150", repl.eval("15").resultValue)
124124
}
125+
126+
@Test
127+
fun `rendering processor should work fine`() {
128+
val repl = makeRepl()
129+
repl.eval(
130+
"""
131+
class A
132+
class B(val a: A)
133+
134+
USE {
135+
render<A> { "iA" }
136+
renderWithHost<B> { host, value -> "iB: " + notebook!!.renderersProcessor.renderValue(host, value.a) }
137+
}
138+
""".trimIndent()
139+
)
140+
141+
val result = repl.eval("B(A())")
142+
assertEquals("iB: iA", result.resultValue)
143+
}
125144
}

‎src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/testUtil.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.jetbrains.kotlinx.jupyter.api.DisplayResultWithCell
1111
import org.jetbrains.kotlinx.jupyter.api.JREInfoProvider
1212
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion
1313
import org.jetbrains.kotlinx.jupyter.api.Notebook
14+
import org.jetbrains.kotlinx.jupyter.api.TypeRenderersProcessor
1415
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
1516
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
1617
import org.jetbrains.kotlinx.jupyter.config.defaultRepositories
@@ -59,7 +60,7 @@ fun assertStartsWith(expectedPrefix: String, actual: String) {
5960
}
6061

6162
fun Collection<Pair<String, String>>.toLibraries(): LibraryResolver {
62-
val libJsons = map { it.first to it.second }.toMap()
63+
val libJsons = associate { it.first to it.second }
6364
return getResolverFromNamesMap(parseLibraryDescriptors(libJsons))
6465
}
6566

@@ -163,6 +164,9 @@ object NotebookMock : Notebook {
163164
get() = defaultRuntimeProperties.version!!
164165
override val jreInfo: JREInfoProvider
165166
get() = JavaRuntime
167+
168+
override val renderersProcessor: TypeRenderersProcessor
169+
get() = error("Not supposed to be called")
166170
}
167171

168172
fun library(builder: JupyterIntegration.Builder.() -> Unit): LibraryDefinition {

0 commit comments

Comments
 (0)
Please sign in to comment.