diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 767fef0490..e7e57cf3a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,8 +52,8 @@ so do familiarize yourself with the following guidelines. ## PR workflow -0. The contributor builds the library locally and runs all unit tests via the Gradle task `dataframe:test` - (see the ["Building"](#building) chapter). +0. The contributor builds the library locally and runs all unit tests via the Gradle task + `dataframe:test -Pkotlin.dataframe.debug=true` (see the ["Building"](#building) chapter). 1. The contributor submits the PR if the local build is successful and the tests are green. 2. The reviewer puts their name in the "Reviewers" section of the proposed PR at the start of the review process. 3. The reviewer leaves comments or marks the PR with the abbreviation "LGTM" (Looks good to me). @@ -103,6 +103,8 @@ This library is built with Gradle. * Run `./gradlew build` to build. It also runs all the tests and checks the linter. * Run `./gradlew :test` to test the module you are looking at to speed things up during development. +* Make sure to pass the extra parameter `-Pkotlin.dataframe.debug=true` to enable debug mode. This flag will + make sure some extra checks are run, which are important but too heavy for production. You can import this project into IDEA, but you have to delegate the build actions to Gradle (in Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle -> Runner) diff --git a/build.gradle.kts b/build.gradle.kts index 14f893ec2d..fa78e51e24 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask +import com.github.gmazzo.buildconfig.BuildConfigExtension import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlinx.dataframe.AnyFrame @@ -25,6 +26,7 @@ plugins { alias(docProcessor) apply false alias(simpleGit) apply false alias(dependencyVersions) + alias(buildconfig) apply false // dependence on our own plugin alias(dataframe) apply false @@ -154,6 +156,18 @@ allprojects { // set the java toolchain version to 11 for all subprojects for CI stability extensions.findByType()?.jvmToolchain(11) + + // Attempts to configure buildConfig for each sub-project that uses it + try { + configure { + packageName = "org.jetbrains.kotlinx.dataframe" + className = "BuildConfig" + buildConfigField("VERSION", "${project.version}") + buildConfigField("DEBUG", findProperty("kotlin.dataframe.debug")?.toString()?.toBoolean() ?: false) + } + } catch (_: UnknownDomainObjectException) { + logger.warn("Could not set buildConfig on :${this.name}") + } } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 3deb7bb460..cfa3a39ade 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -19,6 +19,7 @@ plugins { alias(ktlint) alias(docProcessor) alias(simpleGit) + alias(buildconfig) // dependence on our own plugin alias(dataframe) diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt index cce956c1c4..aef3f08b83 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/columns/DataColumnImpl.kt @@ -1,8 +1,13 @@ package org.jetbrains.kotlinx.dataframe.impl.columns +import org.jetbrains.kotlinx.dataframe.BuildConfig import org.jetbrains.kotlinx.dataframe.DataColumn import org.jetbrains.kotlinx.dataframe.api.dataFrameOf +import org.jetbrains.kotlinx.dataframe.impl.isArray +import org.jetbrains.kotlinx.dataframe.impl.isPrimitiveArray +import kotlin.reflect.KClass import kotlin.reflect.KType +import kotlin.reflect.full.isSubclassOf internal abstract class DataColumnImpl( protected val values: List, @@ -12,6 +17,31 @@ internal abstract class DataColumnImpl( ) : DataColumn, DataColumnInternal { + private infix fun T?.matches(type: KType) = + when { + this == null -> type.isMarkedNullable + + this.isPrimitiveArray -> + type.isPrimitiveArray && + this!!::class.qualifiedName == type.classifier?.let { (it as KClass<*>).qualifiedName } + + this.isArray -> type.isArray + + // cannot check the precise type of array + else -> this!!::class.isSubclassOf(type.classifier as KClass<*>) + } + + init { + // Check for [Issue #713](https://github.com/Kotlin/dataframe/issues/713). + // This only runs with `kotlin.dataframe.debug=true` in gradle.properties. + if (BuildConfig.DEBUG) { + require(values.all { it matches type }) { + val types = values.map { if (it == null) "Nothing?" else it!!::class.simpleName }.distinct() + "Values of column '$name' have types '$types' which are not compatible given with column type '$type'" + } + } + } + protected val distinct = distinct ?: lazy { values.toSet() } override fun name() = name diff --git a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/JupyterConfiguration.kt b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/JupyterConfiguration.kt index 4376bec73b..c9fce63c67 100644 --- a/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/JupyterConfiguration.kt +++ b/core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/JupyterConfiguration.kt @@ -1,10 +1,14 @@ package org.jetbrains.kotlinx.dataframe.jupyter +import org.jetbrains.kotlinx.dataframe.BuildConfig import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration public class JupyterConfiguration { public val display: DisplayConfiguration = DisplayConfiguration() + /** Version of the library. */ + public val version: String = BuildConfig.VERSION + /** DSL accessor. */ public operator fun invoke(block: JupyterConfiguration.() -> Unit): JupyterConfiguration = apply(block) } diff --git a/gradle.properties b/gradle.properties index faf44dfd03..6fcbca9f35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,3 +11,8 @@ org.gradle.jvmargs=-Xmx4G # This makes it mandatory to explicitly apply your own version of the # KSP plugin in the modules that use it. kotlin.dataframe.add.ksp=false + +# Enables debug mode for dataframe. +# This can make certain tests run that should not be run in production. +# It can also be turned on from the command line with `-Pkotlin.dataframe.debug=true` +kotlin.dataframe.debug=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 556592e461..3d752f77d7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,6 +57,7 @@ android-gradle-api = "7.3.1" # Can't be updated to 7.4.0+ due to Java 8 compatib ktor-server-netty = "2.3.8" kotlin-compile-testing = "1.6.0" duckdb = "0.10.0" +buildconfig = "5.4.0" [libraries] ksp-gradle = { group = "com.google.devtools.ksp", name = "symbol-processing-gradle-plugin", version.ref = "ksp" } @@ -149,3 +150,4 @@ kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } dependencyVersions = { id = "com.github.ben-manes.versions", version.ref = "dependencyVersions" } plugin-publish = { id = "com.gradle.plugin-publish", version.ref = "plugin-publish" } shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } +buildconfig = { id = "com.github.gmazzo.buildconfig", version.ref = "buildconfig" }