From e05feac48b726a1863d89b2bf9cf40376d19e4e0 Mon Sep 17 00:00:00 2001 From: Jolan Rensen Date: Wed, 10 Apr 2024 12:30:35 +0200 Subject: [PATCH] kotlin 2.0.0-RC1, enabled jupyter module. Added basic Sparkify conversion for jupyter. Disabled html renderes in favor of just outputting them as text. Notebooks can render them however they like. RDDs are converted to ds before rendering --- buildSrc/src/main/kotlin/Versions.kt | 2 +- .../DataClassSparkifySuperTypeGenerator.kt | 5 +- examples/build.gradle.kts | 6 + gradle/bootstraps/compiler-plugin.jar | Bin 47047 -> 47032 bytes gradle/bootstraps/gradle-plugin.jar | Bin 9345 -> 9347 bytes .../kotlinx/spark/api/jupyter/Integration.kt | 172 ++++++++++++++---- .../spark/api/jupyter/SparkIntegration.kt | 5 +- .../kotlinx/spark/api/jupyter/JupyterTests.kt | 94 ++++++---- settings.gradle.kts | 4 +- 9 files changed, 203 insertions(+), 85 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 59eab276..ed3ce444 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ object Versions : Dsl { const val project = "2.0.0-SNAPSHOT" const val kotlinSparkApiGradlePlugin = "2.0.0-SNAPSHOT" const val groupID = "org.jetbrains.kotlinx.spark" - const val kotlin = "2.0.0-Beta5" + const val kotlin = "2.0.0-RC1" const val jvmTarget = "8" const val jupyterJvmTarget = "8" inline val spark get() = System.getProperty("spark") as String diff --git a/compiler-plugin/src/main/kotlin/org/jetbrains/kotlinx/spark/api/compilerPlugin/fir/DataClassSparkifySuperTypeGenerator.kt b/compiler-plugin/src/main/kotlin/org/jetbrains/kotlinx/spark/api/compilerPlugin/fir/DataClassSparkifySuperTypeGenerator.kt index 339b3269..3a13c458 100644 --- a/compiler-plugin/src/main/kotlin/org/jetbrains/kotlinx/spark/api/compilerPlugin/fir/DataClassSparkifySuperTypeGenerator.kt +++ b/compiler-plugin/src/main/kotlin/org/jetbrains/kotlinx/spark/api/compilerPlugin/fir/DataClassSparkifySuperTypeGenerator.kt @@ -33,10 +33,10 @@ class DataClassSparkifySuperTypeGenerator( } } - context(TypeResolveServiceContainer) override fun computeAdditionalSupertypes( classLikeDeclaration: FirClassLikeDeclaration, - resolvedSupertypes: List + resolvedSupertypes: List, + typeResolver: TypeResolveService, ): List = listOf( buildResolvedTypeRef { val scalaProduct = productFqNames.first().let { @@ -48,7 +48,6 @@ class DataClassSparkifySuperTypeGenerator( isNullable = false, ) } - ) override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean = diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 4a0a2179..8683926a 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -3,7 +3,13 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { // Needs to be installed in the local maven repository or have the bootstrap jar on the classpath id("org.jetbrains.kotlinx.spark.api") + java kotlin("jvm") + kotlin("plugin.noarg") version Versions.kotlin +} + +noArg { + annotation("org.jetbrains.kotlinx.spark.examples.NoArg") } kotlinSparkApi { diff --git a/gradle/bootstraps/compiler-plugin.jar b/gradle/bootstraps/compiler-plugin.jar index 2ea26a00e4761153ad5ca395caecf07e160af1d3..af6fb778737690d95bd62bfbe96d5a4fdf668ac4 100644 GIT binary patch delta 27779 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 diff --git a/gradle/bootstraps/gradle-plugin.jar b/gradle/bootstraps/gradle-plugin.jar index 7d1d03583c8455b18df1d685c5e79520b1ef63c2..fe0cde250c326c54b81d7fb2fbc7f7483f384527 100644 GIT binary patch delta 2705 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 V{kac-o#?}$r;H%y8O-j#e*>yRAa(!% delta 2708 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 XzfhjDi!J7ItBLUzW1-O&ynOW!A} = arrayOf( + "org.jetbrains.kotlinx.spark.api.plugin.annotations.*", "org.jetbrains.kotlinx.spark.api.*", "org.jetbrains.kotlinx.spark.api.tuples.*", *(1..22).map { "scala.Tuple$it" }.toTypedArray(), @@ -116,6 +136,9 @@ abstract class Integration(private val notebook: Notebook, private val options: "org.apache.spark.streaming.*", ) + // Needs to be set by integration + var spark: SparkSession? = null + override fun Builder.onLoaded() { dependencies(*dependencies) import(*imports) @@ -135,27 +158,6 @@ abstract class Integration(private val notebook: Notebook, private val options: ) ) - @Language("kts") - val _0 = execute( - """ - @Deprecated("Use ${displayLimitName}=${properties.displayLimit} in %use magic or ${sparkPropertiesName}.${displayLimitName} = ${properties.displayLimit} instead", ReplaceWith("${sparkPropertiesName}.${displayLimitName}")) - var $displayLimitOld: Int - get() = ${sparkPropertiesName}.${displayLimitName} - set(value) { - println("$displayLimitOld is deprecated: Use ${sparkPropertiesName}.${displayLimitName} instead") - ${sparkPropertiesName}.${displayLimitName} = value - } - - @Deprecated("Use ${displayTruncateName}=${properties.displayTruncate} in %use magic or ${sparkPropertiesName}.${displayTruncateName} = ${properties.displayTruncate} instead", ReplaceWith("${sparkPropertiesName}.${displayTruncateName}")) - var $displayTruncateOld: Int - get() = ${sparkPropertiesName}.${displayTruncateName} - set(value) { - println("$displayTruncateOld is deprecated: Use ${sparkPropertiesName}.${displayTruncateName} instead") - ${sparkPropertiesName}.${displayTruncateName} = value - } - """.trimIndent() - ) - onLoaded() } @@ -180,27 +182,119 @@ abstract class Integration(private val notebook: Notebook, private val options: onShutdown() } + onClassAnnotation { + for (klass in it) { + if (klass.isData) { + execute(generateSparkifyClass(klass)) + } + } + } // Render Dataset render> { - with(properties) { - HTML(it.toHtml(limit = displayLimit, truncate = displayTruncate)) - } + renderDataset(it) } - render> { - with(properties) { - HTML(it.toJavaRDD().toHtml(limit = displayLimit, truncate = displayTruncate)) + // using compile time KType, convert this JavaRDDLike to Dataset and render it + notebook.renderersProcessor.registerWithoutOptimizing( + createRendererByCompileTimeType> { + if (spark == null) return@createRendererByCompileTimeType it.value.toString() + + val rdd = (it.value as JavaRDDLike<*, *>).rdd() + val type = when { + it.type.isSubtypeOf(typeOf()) -> + typeOf() + + it.type.isSubtypeOf(typeOf>()) -> + Tuple2::class.createType( + listOf( + it.type.arguments.first(), + it.type.arguments.last(), + ) + ) + + it.type.isSubtypeOf(typeOf>()) -> + it.type.arguments.first().type!! + + else -> it.type.arguments.first().type!! + } + val ds = spark!!.createDataset(rdd, kotlinEncoderFor(type)) + renderDataset(ds) } - } + ) + + // using compile time KType, convert this RDD to Dataset and render it + notebook.renderersProcessor.registerWithoutOptimizing( + createRendererByCompileTimeType> { + if (spark == null) return@createRendererByCompileTimeType it.value.toString() - render> { - with(properties) { - HTML(it.toHtml(limit = displayLimit, truncate = displayTruncate)) + val rdd = it.value as RDD<*> + val type = it.type.arguments.first().type!! + val ds = spark!!.createDataset(rdd, kotlinEncoderFor(type)) + renderDataset(ds) } + ) + + onLoadedAlsoDo() + } + private fun renderDataset(it: Dataset<*>): MimeTypedResult = + with(properties) { + val showFunction = Dataset::class + .memberFunctions + .firstOrNull { it.name == "showString" && it.valueParameters.size == 3 } + + textResult( + if (showFunction != null) { + showFunction.call(it, displayLimit, displayTruncate, false) as String + } else { + // if the function cannot be called, make sure it will call println instead + it.show(displayLimit, displayTruncate) + "" + } + ) } - onLoadedAlsoDo() + + // TODO wip + private fun generateSparkifyClass(klass: KClass<*>): Code { +// val name = "`${klass.simpleName!!}${'$'}Generated`" + val name = klass.simpleName + val constructorArgs = klass.primaryConstructor!!.parameters + val visibility = klass.visibility?.name?.lowercase() ?: "" + val memberProperties = klass.memberProperties + + val properties = constructorArgs.associateWith { + memberProperties.first { it.name == it.name } + } + + val constructorParamsCode = properties.entries.joinToString("\n") { (param, prop) -> + // TODO check override + if (param.isOptional) TODO() + val modifier = if (prop is KMutableProperty<*>) "var" else "val" + val paramVisiblity = prop.visibility?.name?.lowercase() ?: "" + val columnName = param.findAnnotation()?.name ?: param.name!! + + "| @get:kotlin.jvm.JvmName(\"$columnName\") $paramVisiblity $modifier ${param.name}: ${param.type}," + } + + val productElementWhenParamsCode = properties.entries.joinToString("\n") { (param, _) -> + "| ${param.index} -> this.${param.name}" + } + + @Language("kotlin") + val code = """ + |$visibility data class $name( + $constructorParamsCode + |): scala.Product, java.io.Serializable { + | override fun canEqual(that: Any?): Boolean = that is $name + | override fun productArity(): Int = ${constructorArgs.size} + | override fun productElement(n: Int): Any = when (n) { + $productElementWhenParamsCode + | else -> throw IndexOutOfBoundsException() + | } + |} + """.trimMargin() + return code } } diff --git a/jupyter/src/main/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/SparkIntegration.kt b/jupyter/src/main/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/SparkIntegration.kt index cc308116..0c4eb096 100644 --- a/jupyter/src/main/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/SparkIntegration.kt +++ b/jupyter/src/main/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/SparkIntegration.kt @@ -25,6 +25,7 @@ package org.jetbrains.kotlinx.spark.api.jupyter import org.intellij.lang.annotations.Language import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost import org.jetbrains.kotlinx.jupyter.api.Notebook +import org.jetbrains.kotlinx.spark.api.SparkSession import org.jetbrains.kotlinx.spark.api.jupyter.Properties.Companion.appNameName import org.jetbrains.kotlinx.spark.api.jupyter.Properties.Companion.sparkMasterName @@ -86,7 +87,7 @@ class SparkIntegration(notebook: Notebook, options: MutableMap) """ inline fun dfOf(vararg arg: T): Dataset = spark.dfOf(*arg)""".trimIndent(), """ - inline fun emptyDataset(): Dataset = spark.emptyDataset(encoder())""".trimIndent(), + inline fun emptyDataset(): Dataset = spark.emptyDataset(kotlinEncoderFor())""".trimIndent(), """ inline fun dfOf(colNames: Array, vararg arg: T): Dataset = spark.dfOf(colNames, *arg)""".trimIndent(), """ @@ -108,6 +109,8 @@ class SparkIntegration(notebook: Notebook, options: MutableMap) """ inline fun > UserDefinedFunction.register(name: String): NAMED_UDF = spark.udf().register(name = name, udf = this)""".trimIndent(), ).map(::execute) + + spark = execute("spark").value as SparkSession } override fun KotlinKernelHost.onShutdown() { diff --git a/jupyter/src/test/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/JupyterTests.kt b/jupyter/src/test/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/JupyterTests.kt index b82512b7..3ffd37be 100644 --- a/jupyter/src/test/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/JupyterTests.kt +++ b/jupyter/src/test/kotlin/org/jetbrains/kotlinx/spark/api/jupyter/JupyterTests.kt @@ -32,15 +32,12 @@ import org.apache.spark.api.java.JavaSparkContext import org.apache.spark.streaming.api.java.JavaStreamingContext import org.intellij.lang.annotations.Language import org.jetbrains.kotlinx.jupyter.EvalRequestData -import org.jetbrains.kotlinx.jupyter.MutableNotebook import org.jetbrains.kotlinx.jupyter.ReplForJupyter -import org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl import org.jetbrains.kotlinx.jupyter.api.Code import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult -import org.jetbrains.kotlinx.jupyter.libraries.EmptyResolutionInfoProvider +import org.jetbrains.kotlinx.jupyter.api.MimeTypes import org.jetbrains.kotlinx.jupyter.repl.EvalResultEx import org.jetbrains.kotlinx.jupyter.repl.creating.createRepl -import org.jetbrains.kotlinx.jupyter.testkit.JupyterReplTestCase import org.jetbrains.kotlinx.jupyter.testkit.ReplProvider import org.jetbrains.kotlinx.jupyter.util.PatternNameAcceptanceRule import org.jetbrains.kotlinx.spark.api.SparkSession @@ -83,10 +80,11 @@ class JupyterTests : ShouldSpec({ context("Jupyter") { withRepl { + exec("%trackExecution") should("Allow functions on local data classes") { @Language("kts") - val klass = exec("""data class Test(val a: Int, val b: String)""") + val klass = exec("""@Sparkify data class Test(val a: Int, val b: String)""") @Language("kts") val ds = exec("""val ds = dsOf(Test(1, "hi"), Test(2, "something"))""") @@ -112,7 +110,7 @@ class JupyterTests : ShouldSpec({ should("render Datasets") { @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val ds = listOf(1, 2, 3).toDS() ds @@ -128,7 +126,7 @@ class JupyterTests : ShouldSpec({ should("render JavaRDDs") { @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val rdd: JavaRDD> = listOf( listOf(1, 2, 3), @@ -145,7 +143,7 @@ class JupyterTests : ShouldSpec({ should("render JavaRDDs with Arrays") { @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val rdd: JavaRDD = rddOf( intArrayOf(1, 2, 3), @@ -165,7 +163,7 @@ class JupyterTests : ShouldSpec({ @Language("kts") val klass = exec( """ - data class Test( + @Sparkify data class Test( val longFirstName: String, val second: LongArray, val somethingSpecial: Map, @@ -174,7 +172,7 @@ class JupyterTests : ShouldSpec({ ) @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val rdd = listOf( @@ -185,29 +183,40 @@ class JupyterTests : ShouldSpec({ rdd """.trimIndent() ) - html shouldContain "Test(longFirstName=aaaaaaaa..." + html shouldContain """ + +-------------+---------------+--------------------+ + |longFirstName| second| somethingSpecial| + +-------------+---------------+--------------------+ + | aaaaaaaaa|[1, 100000, 24]|{1 -> one, 2 -> two}| + | aaaaaaaaa|[1, 100000, 24]|{1 -> one, 2 -> two}| + +-------------+---------------+--------------------+""".trimIndent() } should("render JavaPairRDDs") { @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val rdd: JavaPairRDD = rddOf( - c(1, 2).toTuple(), - c(3, 4).toTuple(), + t(1, 2), + t(3, 4), ).toJavaPairRDD() rdd """.trimIndent() ) println(html) - html shouldContain "1, 2" - html shouldContain "3, 4" + html shouldContain """ + +---+---+ + | _1| _2| + +---+---+ + | 1| 2| + | 3| 4| + +---+---+""".trimIndent() } should("render JavaDoubleRDD") { @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val rdd: JavaDoubleRDD = rddOf(1.0, 2.0, 3.0, 4.0,).toJavaDoubleRDD() rdd @@ -223,7 +232,7 @@ class JupyterTests : ShouldSpec({ should("render Scala RDD") { @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ val rdd: RDD> = rddOf( listOf(1, 2, 3), @@ -244,9 +253,9 @@ class JupyterTests : ShouldSpec({ val oldTruncation = exec("""sparkProperties.displayTruncate""") as Int @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ - data class Test(val a: String) + @Sparkify data class Test(val a: String) sparkProperties.displayTruncate = 3 dsOf(Test("aaaaaaaaaa")) """.trimIndent() @@ -255,8 +264,8 @@ class JupyterTests : ShouldSpec({ @Language("kts") val restoreTruncation = exec("""sparkProperties.displayTruncate = $oldTruncation""") - html shouldContain "aaa" - html shouldNotContain "aaaaaaaaaa" + html shouldContain "aaa" + html shouldNotContain "aaaaaaaaaa" } should("limit dataset rows using properties") { @@ -265,9 +274,9 @@ class JupyterTests : ShouldSpec({ val oldLimit = exec("""sparkProperties.displayLimit""") as Int @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ - data class Test(val a: String) + @Sparkify data class Test(val a: String) sparkProperties.displayLimit = 3 dsOf(Test("a"), Test("b"), Test("c"), Test("d"), Test("e")) """.trimIndent() @@ -276,11 +285,11 @@ class JupyterTests : ShouldSpec({ @Language("kts") val restoreLimit = exec("""sparkProperties.displayLimit = $oldLimit""") - html shouldContain "a" - html shouldContain "b" - html shouldContain "c" - html shouldNotContain "d" - html shouldNotContain "e" + html shouldContain "a|" + html shouldContain "b|" + html shouldContain "c|" + html shouldNotContain "d|" + html shouldNotContain "e|" } should("truncate rdd cells using properties") { @@ -289,7 +298,7 @@ class JupyterTests : ShouldSpec({ val oldTruncation = exec("""sparkProperties.displayTruncate""") as Int @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ sparkProperties.displayTruncate = 3 rddOf("aaaaaaaaaa") @@ -299,8 +308,8 @@ class JupyterTests : ShouldSpec({ @Language("kts") val restoreTruncation = exec("""sparkProperties.displayTruncate = $oldTruncation""") - html shouldContain "aaa" - html shouldNotContain "aaaaaaaaaa" + html shouldContain "aaa" + html shouldNotContain "aaaaaaaaaa" } should("limit rdd rows using properties") { @@ -309,7 +318,7 @@ class JupyterTests : ShouldSpec({ val oldLimit = exec("""sparkProperties.displayLimit""") as Int @Language("kts") - val html = execHtml( + val html = execForDisplayText( """ sparkProperties.displayLimit = 3 rddOf("a", "b", "c", "d", "e") @@ -319,11 +328,11 @@ class JupyterTests : ShouldSpec({ @Language("kts") val restoreLimit = exec("""sparkProperties.displayLimit = $oldLimit""") - html shouldContain "a" - html shouldContain "b" - html shouldContain "c" - html shouldNotContain "d" - html shouldNotContain "e" + html shouldContain " a|" + html shouldContain " b|" + html shouldContain " c|" + html shouldNotContain " d|" + html shouldNotContain " e|" } @Language("kts") @@ -391,7 +400,7 @@ class JupyterStreamingTests : ShouldSpec({ } } - xshould("stream") { + should("stream") { @Language("kts") val value = exec( @@ -458,4 +467,11 @@ private fun ReplForJupyter.execHtml(code: Code): String { return html } +private fun ReplForJupyter.execForDisplayText(code: Code): String { + val res = exec(code) + val text = res[MimeTypes.PLAIN_TEXT] + text.shouldNotBeNull() + return text +} + class Counter(@Volatile var value: Int) : Serializable diff --git a/settings.gradle.kts b/settings.gradle.kts index 8ad32812..98776e06 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -35,7 +35,7 @@ rootProject.name = "kotlin-spark-api-parent_$versions" include("scala-helpers") include("scala-tuples-in-kotlin") include("kotlin-spark-api") -//include("jupyter") +include("jupyter") include("examples") include("compiler-plugin") include("gradle-plugin") @@ -46,7 +46,7 @@ project(":scala-tuples-in-kotlin").name = "scala-tuples-in-kotlin_$scalaCompat" // spark+scala dependent project(":kotlin-spark-api").name = "kotlin-spark-api_$versions" -//project(":jupyter").name = "jupyter_$versions" +project(":jupyter").name = "jupyter_$versions" project(":examples").name = "examples_$versions" buildCache {