Skip to content

Commit

Permalink
Merge pull request #965 from Kotlin/buggy-extension-property-exception
Browse files Browse the repository at this point in the history
Clarify the situation when generated extension property causes ClassCast or NPE exceptions
  • Loading branch information
koperagen authored Dec 3, 2024
2 parents b116126 + cd2951a commit 4fd1eaf
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 1 deletion.
5 changes: 5 additions & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -9918,6 +9918,11 @@ public final class org/jetbrains/kotlinx/dataframe/exceptions/ColumnNotFoundExce
public fun getMessage ()Ljava/lang/String;
}

public final class org/jetbrains/kotlinx/dataframe/exceptions/ColumnTypeMismatchesColumnValuesException : java/lang/RuntimeException {
public fun <init> (Lorg/jetbrains/kotlinx/dataframe/DataColumn;Ljava/lang/Throwable;)V
public final fun getColumn ()Lorg/jetbrains/kotlinx/dataframe/DataColumn;
}

public final class org/jetbrains/kotlinx/dataframe/exceptions/DuplicateColumnNamesException : java/lang/IllegalArgumentException {
public fun <init> (Ljava/util/List;)V
public final fun getAllColumnNames ()Ljava/util/List;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.jetbrains.kotlinx.dataframe.exceptions

import org.jetbrains.kotlinx.dataframe.AnyCol
import org.jetbrains.kotlinx.dataframe.DataColumn
import org.jetbrains.kotlinx.dataframe.columns.values
import org.jetbrains.kotlinx.dataframe.type

/**
* Extension properties are generated according to [DataColumn.type] property
* [DataColumn.type] must match types of [DataColumn.values], but it can fail to do so.
* This causes [ClassCastException] or [NullPointerException] when you use extension property and actual value is of different type or is null.
* If generated extension property causes this exception, this is a bug in the library
* You can work around this problem by referring to [column] using String API
*/
public class ColumnTypeMismatchesColumnValuesException(public val column: AnyCol, cause: Throwable) :
RuntimeException("Failed to convert column '${column.name()}'", cause)
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import org.jetbrains.kotlinx.dataframe.columns.values
import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME
import org.jetbrains.kotlinx.dataframe.dataTypes.IMG
import org.jetbrains.kotlinx.dataframe.exceptions.CellConversionException
import org.jetbrains.kotlinx.dataframe.exceptions.ColumnTypeMismatchesColumnValuesException
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
import org.jetbrains.kotlinx.dataframe.impl.columns.DataColumnInternal
Expand Down Expand Up @@ -68,7 +69,16 @@ internal fun <T, C, R> Convert<T, C>.withRowCellImpl(
type: KType,
infer: Infer,
rowConverter: RowValueExpression<T, C, R>,
): DataFrame<T> = to { col -> df.newColumn(type, col.name, infer) { rowConverter(it, it[col]) } }
): DataFrame<T> =
to { col ->
try {
df.newColumn(type, col.name, infer) { rowConverter(it, it[col]) }
} catch (e: ClassCastException) {
throw ColumnTypeMismatchesColumnValuesException(col, e)
} catch (e: NullPointerException) {
throw ColumnTypeMismatchesColumnValuesException(col, e)
}
}

@PublishedApi
internal fun <T, C, R> Convert<T, C>.convertRowColumnImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import io.kotest.matchers.shouldBe
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalTime
import org.jetbrains.kotlinx.dataframe.ColumnsContainer
import org.jetbrains.kotlinx.dataframe.DataColumn
import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
import org.jetbrains.kotlinx.dataframe.exceptions.CellConversionException
import org.jetbrains.kotlinx.dataframe.exceptions.ColumnTypeMismatchesColumnValuesException
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
import org.jetbrains.kotlinx.dataframe.hasNulls
Expand Down Expand Up @@ -315,4 +317,15 @@ class ConvertTests {
}
}
}

private interface Marker

private val ColumnsContainer<Marker>.a get() = this["a"] as DataColumn<String>

@Test
fun `convert with buggy extension property`() {
shouldThrow<ColumnTypeMismatchesColumnValuesException> {
dataFrameOf("a")(1, 2, 3).cast<Marker>().convert { a }.with { it }
}
}
}

0 comments on commit 4fd1eaf

Please sign in to comment.