Skip to content

Commit

Permalink
Clarify the situation when generated extension property causes ClassC…
Browse files Browse the repository at this point in the history
…ast or NPE exceptions
  • Loading branch information
koperagen committed Nov 25, 2024
1 parent c7b1004 commit 7eaf070
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 1 deletion.
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 ColumnValuesMismatchColumnTypeException(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 @@ -31,6 +31,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.ColumnValuesMismatchColumnTypeException
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 @@ -60,7 +61,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 ColumnValuesMismatchColumnTypeException(col, e)
} catch (e: NullPointerException) {
throw ColumnValuesMismatchColumnTypeException(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.ColumnValuesMismatchColumnTypeException
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
import org.jetbrains.kotlinx.dataframe.hasNulls
Expand Down Expand Up @@ -172,4 +174,15 @@ class ConvertTests {
val res = col.convertTo<String>()
res.print()
}

private interface Marker

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

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

0 comments on commit 7eaf070

Please sign in to comment.